Here, you can get source code.
Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable.
Vue
Using IDEA plugin:
Using Vue, You can create an index.html
file and include Vue with:
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
or:
<!-- production version, optimized for size and speed -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
Declarative Rendering
At the core of Vue.js is a system that enables us to declaratively render data to the DOM using straightforward template syntax:
<div id="app">
{{ message }}
</div>
You can offer the data by:
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
Run it:
Hello Vue!
The data and the DOM are now linked, and everything is now reactive. How do we know? Open your browser’s JavaScript console (right now, on this page) and set app.message
to a different value. You should see the rendered example above update accordingly.
Note that we no longer have to interact with the HTML directly. A Vue app attaches itself to a single DOM element (#app
in our case) then fully controls it.
In addition to text interpolation, we can also bind element attributes like this:
<div id="app-2">
<span v-bind:title="message">
Hover your mouse over me for a few seconds
to see my dynamically bound title!
</span>
</div>
var app2 = new Vue({
el: '#app-2',
data: {
message: 'You loaded this page on ' + new Date().toLocaleString()
}
})
Hover your mouse over me for a few seconds to see dynamically bound title.
Here we are encountering something new. The v-bind
attribute you are seeing is called a directive. Directives are prefixed with v-
to indicate that they are special attributes provided by Vue, and as you may have guessed, they apply special reactive behavior to the rendered DOM. Here, it is basically saying “keep this element’s title
attribute up-to-date with the message
property on the Vue instance.”
If you open up your JavaScript console again and enter app2.message = 'some new message'
, you’ll once again see that the bound HTML - in this case the title
attribute - has been updated like before.
Conditionals and Loops
It’s easy to toggle the presence of an element, too:
<div id="app-3">
<span v-if="seen">Now you see me</span>
</div>
var app3 = new Vue({
el: '#app-3',
data: {
seen: true
}
})
result:
Now you see me
Go ahead and enter app3.seen = false
in the console. You should see the message disappear.
There are quite a few other directives, each with its own special functionality. For example, the v-for
directive can be used for displaying a list of items using the data from an Array:
<div id="app-4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
var app4 = new Vue({
el: '#app-4',
data: {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something awesome' }
]
}
})
result:
In the console, enter app4.todos.push({ text: 'New item' })
. You should see a new item appended to the list.
Handling User Input
To let users interact with your app, we can use the v-on
directive to attach event listeners that invoke methods on our Vue instances:
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
</div>
var app5 = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
Note that in this method we update the state of our app without touching the DOM - all DOM manipulations are handled by Vue, and the code you write is focused on the underlying logic.
Vue also provides the v-model
directive that makes two-way binding between form input and app state a breeze:
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
Composing with Components
The component system is another important concept in Vue, because it’s an abstraction that allows us to build large-scale applications composed of small, self-contained, and often reusable components. If we think about it, almost any type of application interface can be abstracted into a tree of components:
In Vue, a component is essentially a Vue instance with pre-defined options. Registering a component in Vue is straightforward:
// Define a new component called todo-item
Vue.component('todo-item', {
template: '<li>This is a todo</li>'
})
var app = new Vue(...)
Now you can compose it in another component’s template:
<ol>
<!-- Create an instance of the todo-item component -->
<todo-item></todo-item>
</ol>
But this would render the same text for every todo, which is not super interesting. We should be able to pass data from the parent scope into child components. Let’s modify the component definition to make it accept a prop:
Vue.component('todo-item', {
// The todo-item component now accepts a
// "prop", which is like a custom attribute.
// This prop is called todo.
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
Now we can pass the todo into each repeated component using v-bind
:
<div id="app-7">
<ol>
<!--
Now we provide each todo-item with the todo object
it's representing, so that its content can be dynamic.
We also need to provide each component with a "key",
which will be explained later.
-->
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id"
></todo-item>
</ol>
</div>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: 'Vegetables' },
{ id: 1, text: 'Cheese' },
{ id: 2, text: 'Whatever else humans are supposed to eat' }
]
}
})
run it:
This is a contrived example, but we have managed to separate our app into two smaller units, and the child is reasonably well-decoupled from the parent via the props interface. We can now further improve our <todo-item>
component with more complex template and logic without affecting the parent app.
In a large application, it is necessary to divide the whole app into components to make development manageable. We will talk a lot more about components later in the guide, but here’s an (imaginary) example of what an app’s template might look like with components:
<div id="app">
<app-nav></app-nav>
<app-view>
<app-sidebar></app-sidebar>
<app-content></app-content>
</app-view>
</div>
Vue-Axios
Axios是一个开源的可以在浏览器端和NodeJS的异步通信框架,主要作用是实现Ajax异步通信。
准备一份JSON文件:
{
"name": "Q",
"url": "https://hellooooo.top",
"page": 1,
"liangzai": true
}
使用Vue的钩子函数进行Axios异步请求:
var vue = new Vue({
el: '#app',
data: {
message: "Hello"
},
//钩子
mounted(){
axios.get('/Vue/vue-first/data.json').then(response => console.log(response.data));
}
});
结果:
将Axios得到的数据与页面进行绑定:
var vue = new Vue({
el: '#app',
data() {
return{
info: {
name: null,
url: null,
liangzai: null
}
}
},
//钩子
mounted(){
// axios.get('/Vue/vue-first/data.json').then(response => console.log(response.data));
axios.get('/Vue/vue-first/data.json').then(response => this.info = response.data);
}
});
页面:
<div id="app">
<span>{{info.name}}</span>
<span>{{info.url}}</span>
<span>{{info.liangzai}}</span>
</div>
结果:
计算属性
有缓存的味道。
var vm = new Vue({
el: '#app',
data: {
message: "Hello"
},
methods: {
currentTime: function (){
return Date.now();
}
},
//计算属性
//注意methods和computed尽量不要重名
computed: {
currentTime2: ()=>{
return Date.now();
}
}
});
<p>current: {{currentTime()}}</p>
<p>current: {{currentTime2}}</p>
运行:
可以看到currentTime方法输出的时间都在变化,而currentTime2每次都输出相同的时间戳。
内容分发
Vue.component("todo",{
template: '<div>' +
'<slot name="todo-title"></slot>' +
'<ul>' +
'<slot name="todo-items"></slot>' +
'</ul>' +
'</div>'
});
Vue.component("todo-title", {
props: ['title'],
template: '<div>{{title}}</div>'
});
Vue.component("todo-items", {
props: ['item'],
template: '<li>{{item}}</li>'
});
var vm = new Vue({
el: '#app',
data: {
title: "liang",
todoItems: [
'Liang0',
'Liang1',
'Liang2',
'Liang3'
]
}
});
下面的":"是"v-bind:"的简写,":title"相当于"v-bind:title"。
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items>
</todo>
</div>
效果:
如果需要在每个项目之后增添一个删除按钮:
Vue.component("todo-items", {
props: ['item'],
template: '<li>{{item}} <button @click="remove">Delete</button></li>',
methods: {
remove: ()=>{
alert("Choose delete.")
}
}
});
var vm = new Vue({
el: '#app',
data: {
title: "liang",
todoItems: [
'Liang0',
'Liang1',
'Liang2',
'Liang3'
]
},
methods:{
removeItems: (index)=>{
console.log("Will remove: " + this.todoItems[index]);
//删除当前
this.todoItems.splice(index, 1);
}
}
});
可以做到监听,但是组件无法做到调用Vue实例的removeItems方法删除数据。
即组件如何删除Vue实例中的数据?
自定义事件
this.$emit
Vue.component("todo",{
template: '<div>' +
'<slot name="todo-title"></slot>' +
'<ul>' +
'<slot name="todo-items"></slot>' +
'</ul>' +
'</div>'
});
Vue.component("todo-title", {
props: ['title'],
template: '<div>{{title}}</div>'
});
Vue.component("todo-items", {
props: ['item', 'index'],
template: '<li>{{index}}:{{item}} <button @click="remove">Delete</button></li>',
methods: {
remove: function (index){
// alert("Choose delete.")
this.$emit('remove', index);
}
}
});
var vm = new Vue({
el: '#app',
data: {
title: "liang",
todoItems: [
'Liang0',
'Liang1',
'Liang2',
'Liang3'
]
},
methods:{
removeItems: function (index){
console.log("Will remove: " + this.todoItems[index]);
//删除当前
this.todoItems.splice(index, 1);
}
}
});
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-items slot="todo-items" v-for="item,index in todoItems" :item="item" :index="index" :key="index" v-on:remove="removeItems(index)"></todo-items>
</todo>
</div>
Vue-Cli
官方提供的一个脚手架。
先安装Node.js,安装完成后,打开CMD:
C:\Users\Q>node -v
v10.18.0
C:\Users\Q>npm -v
6.13.4
安装淘宝镜像加速器:
# -g 全局安装
npm install cnpm -g
如果不想安装可以在安装的时候增加如下参数:
npm install --registry=https://registry.npm.taobao.org
安装vue-cli:
cnpm install vue-cli -g
查看:
vue list
创建第一个vue-cli应用程序
在命令行执行:
vue init webpack myvue
myvue为项目名称.
初始化并且运行:
cd myvue
npm install
npm run dev
Webpack
安装Webpack:
npm install webpack -g
npm install webpack-cli -g
查看版本:
webpack -v
webpack-cli -v
新建文件夹webpack-study并进入:
mkdir webpack-study
cd webpack-study
新建modules:
mkdir modules
在其内新建一个Hello.js:
//暴露一个方法
exports.sayHi = ()=>{
document.write("<h1>Liangzai</h1>")
};
新建一个main.js:
var hello = require("./hello");
hello.sayHi();
在webpack-study内新建一个webpack.config.js:
module.exports = {
entry: './modules/main.js',
output: {
filename: "./js/bundle.js"
}
};
在命令行输入:
webpack
自动生成了bundle.js:
测试使用:
新建index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
<script src="dist/js/bundle.js"></script>
</body>
</html>
Vue-Router
进入自己项目根目录,安装:
npm install vue-router --save-dev
声明使用Vue-Router
import VueRouter from "vue-router";
//显式声明使用Vue-Router
Vue.use(VueRouter);
简单使用:
新建两个Component,一个Content,一个Main:
<template>
<h1>Content</h1>
</template>
<script>
export default {
name: "Content"
}
</script>
<style scoped>
</style>
<template>
<h1>Main</h1>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
在src目录下新建router目录,并添加index.js作为配置文件:
import Vue from "vue";
import VueRouter from "vue-router";
import Content from "../components/Content";
import Main from "../components/Main";
//安装路由
Vue.use(VueRouter);
//配置导出路由
export default new VueRouter({
routes: [
{
// 路由路径
path: '/content',
name: "content",
// 跳转组件
component: Content
},
{
// 路由路径
path: '/main',
name: "main",
// 跳转组件
component: Main
}
]
})
编辑main.js配置路由:
//自动扫描里的路由配置
import router from './router';
...
//在Vue实例内配置路由
new Vue({
el: '#app',
//在Vue实例配置路由
router,
components: { App },
template: '<App/>'
})
配置App.vue,添加路由跳转:
<template>
<div id="app">
<h1>靓仔</h1>
<router-link to="/main">Main</router-link>
<router-link to="/content">Content</router-link>
<router-view></router-view>
</div>
</template>
其他保持不变,运行测试:
Vue + ElementUI
创建一个名为hello-vue的工程:
vue init webpack hello-vue
进入hello-vue安装vue-router,element-ui,sass-loader和node-sass:
npm install vue-router --save-dev
npm i element-ui -S
#安装依赖
npm install
#安装SASS加载器
npm install sass-loader node-sass --save-dev
#启动测试
npm run dev
准备Main页面以及Login页面,
Main:
<template>
<h1>MAIN</h1>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
Login:
<template>
<div>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>
<el-dialog
title="温馨提示"
:visible.sync="dialogVisible"
width="30%">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
.login-box{
border: 1px solid #DCDFE6;
width: 350px;
margin:180px auto;
padding:35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow:0 0 25px #909399;
}
.login-title{
text-align:center;
margin:0 auto 40px auto;
color:#303133;
}
</style>
<script>
export default {
name:"Login",
data(){
return {
form:{
username: '',
password: ''
},
//表单验证,需要再el-form-item 元素中增加prop属性
rules:{
username:[
{required:true,message:'账号不能为空',trigger:'blur'}
],
password:[
{required: true,message: '密码不能为空',trigger:'blur'}
]
},
//对话框显示和隐藏
dialogVisible:false
}
},
methods:{
onSubmit(formName) {
//为表单绑定验证功能
this.$refs[formName].validate((valid) =>{
if (valid){
//使用 vue-router路由到指定页面,该方式称之为编程式导航
this.$router.push("/main");
} else {
this.dialogVisible = true;
return false;
}
});
}
}
}
</script>
路由配置:
import Vue from "vue";
import VueRouter from "vue-router";
import Main from "../views/Main";
import Login from "../views/Login";
Vue.use(VueRouter);
export default new VueRouter({
routes: [
{
path: '/login',
component: Login
},
{
path: '/main',
component: Main
}
]
});
App.vue配置展示界面:
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
main.js配置:
import Vue from 'vue'
import App from './App'
import router from "./router";
import Element from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.config.productionTip = false
Vue.use(router);
Vue.use(Element);
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App),
components: { App },
template: '<App/>'
})
如果报错:
ERROR Failed to compile with 1 errors
可能是sass版本太高所致:
卸载当前sass版本:
npm uninstall sass-loader
npm install sass-loader@7.3.1 --save-dev
结果:
路由嵌套
替换Main.vue:
<template>
<div>
<el-container>
<el-aside width="200px">
<el-menu :default-openeds="['1']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
<el-menu-item-group>
<el-menu-item index="1-1">
<router-link to="/user/profile">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<router-link to="/user/list">用户列表</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
<e1-menu-item-group>
<el-menu-item index="2-1">分类管理</el-menu-item>
<el-menu-item index="2-2">内容列表</el-menu-item>
</e1-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right:15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
</el-container>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right:15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped lang="scss">
.el-header {
background-color: #048bd1;
color: #333;
line-height: 60px;
}
.el-aside {
color: #333;
}
</style>
我这代码抄得有点问题,但是大致还能看看。
在views下新建user目录并添加List .vue和profile.vue:
<template>
<h1>UserList</h1>
</template>
<script>
export default {
name: "List"
}
</script>
<style scoped>
</style>
<template>
<h1>Profile</h1>
</template>
<script>
export default {
name: "Profile"
}
</script>
<style scoped>
</style>
配置路由组件router/index.js:
import List from "../views/user/List";
import Profile from "../views/user/Profile";
...
export default new VueRouter({
routes: [
{
path: '/login',
component: Login
},
{
path: '/main',
component: Main,//嵌套路由
children: [
{
path: '/user/profile',
component: Profile
},
{
path: '/user/list',
component: List
}
]
}
]
});
测试:
参数传递及重定向
个人信息页面应该因人而异。
修改Main.vue的个人信息的<router-link>:
<router-link :to="{name:'UserProfile',params: {id: 1}}">个人信息</router-link>
这里的name是路由配置router/index.js中添加的name:
{
path: '/user/profile/:id',
name: 'UserProfile',
component: Profile
}
Profile.vue展示这个数字:
<template>
<!-- Component template should contain exactly one root element.-->
<div>
<h1>Profile</h1>
{{$route.params.id}}
</div>
</template>
还可以通过props达到目的
关于重定向:
<el-menu-item index="1-3">
<router-link to="/goHome">GoHOME</router-link>
</el-menu-item>
编辑路由配置index.js:
routes: [
{
path: '/login',
component: Login
},
{
path: '/main',
component: Main,//嵌套路由
children: [
{
path: '/user/profile/:id',
name: 'UserProfile',
component: Profile
},
{
path: '/user/list',
component: List
},
{
path: '/goHome',
redirect: '/main'
}
]
}
]
通过redirect进行即可,结果:
比如,我们登录之后需要把用户名展示在页面,我们可以这样。
将用户名放在main路径之后:
修改Login.vue
//使用 vue-router路由到指定页面,该方式称之为编程式导航
this.$router.push("/main" + "/" + this.form.username);
修改index.js:
path: '/main/:name',
component: Main,//嵌套路由
props: true,
children: [
...
修改Main.vue,展示用户名:
<span>{{name}}</span>
...
export default {
props: ['name'],
name: "Main"
}
运行:
如何去除访问路径中的‘#’?
编辑路由配置文件:
export default new VueRouter({
mode: 'history',//hash
...
将模式修改为'history'即可。
404
如何统一展示404?
在views下新建NotFound.vue:
<template>
<div>
<h1>404,Not Found</h1>
</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
<style scoped>
</style>
在路由配置中添加该组件:
import NotFound from "../views/NotFound";
同时配置路径:
routes: [
{
path: '/login',
component: Login
},
...
{
path: '*',
component: NotFound
}
]
测试:
此外,Vue还提供了各种钩子回调:
export default {
name: "Profile",
beforeRouteEnter: (to, from, next) => {
console.log("进入路由之前");
next();
},
beforeRouteLeave: (to, from, next) => {
console.log("进入路由之后");
next();
}
}
到时候可以再看。