Vue.js 是用于构建交互式的 Web 界面的库。
Vue.js 提供了 MVVM 数据绑定和一个可组合的组件系统,具备简单、灵活的 API。javascript
其实和Jquery同样,VueJs就是一个Js库,可是是面向前端的库,具体来说叫作MVVM(Model-View-ViewModel)库.
也就是说,有部分功能和Jquery是差很少的,Vuejs能作的,Jquery也能作。这下咱们就放心了,Jquery多简单啊,使人发指的是Vuejs在实现相同功能的时候更简单(否则用你干吗啊);css
理解Vuejs最关键的一句话叫作“数据驱动视图”,好比用Jquery来作一个列表,这个列表的数据是从Laravel来的,那么咱们要遍历这个数据,而后把列表的html元素加到dom里面去, 要删除一个列表项的时候,先要在找到列表项在dom的位置,而后去除这个节点。Vuejs不用,数据在的时候,列表就在,数据减一,列表项就自动实时相应减一。也就是说,你只要操做数据就够了,不用管dom。这基本就是Vuejs的中心思想。html
下图不只归纳了MVVM模式(Model-View-ViewModel),还描述了在Vue.js中ViewModel是如何和View以及Model进行交互的。前端
ViewModel是Vue.js的核心,它是一个Vue实例。Vue实例是做用于某一个HTML元素上的,这个元素能够是HTML的body元素,也能够是指定了id的某个元素。vue
当建立了ViewModel后,双向绑定是如何达成的呢?java
首先,咱们将上图中的DOM Listeners和Data Bindings看做两个工具,它们是实现双向绑定的关键。
从View侧看,ViewModel中的DOM Listeners工具会帮咱们监测页面上DOM元素的变化,若是有变化,则更改Model中的数据;
从Model侧看,当咱们更新Model中的数据时,Data Bindings工具会帮咱们更新页面中的DOM元素。node
先来看一个简单的示例,Hello,World!webpack
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue学习</title> <script src="http://static.runoob.com/assets/vue/1.0.11/vue.min.js"></script> </head> <body> <div id="app"> {{ message }} </div> <!-- JavaScript 代码须要放在尾部(指定的HTML元素以后) --> <script> new Vue({ el:'#app', data: { message:'Hello World!' } }); </script> </body> </html>
示例详解:ios
每一个Vue实例都会代理其data对象中的全部属性:laravel
var data = { a: 1 } var vm = new Vue({ data: data }) vm.a === data.a // -> true // setting the property also affects original data vm.a = 2 data.a // -> 2 // ... and vice-versa data.a = 3 vm.a // -> 3
须要注意的是只有代理属性是反应式的,若是在实例建立以后添加一个新的属性到实例上,将不会触发任何视图更新。关于这一点咱们将在后续反应系统中讨论。
除了数据属性以外,Vue实例还提供了许多有用的实例属性和方法,这些属性和方法都以$
开头以便和代理数据属性进行区分。例如:
var data = { a: 1 } var vm = new Vue({ el: '#example', data: data }) vm.$data === data // -> true vm.$el === document.getElementById('example') // -> true // $watch is an instance method vm.$watch('a', function (newVal, oldVal) { // this callback will be called when `vm.a` changes })
每一个Vue实例在建立时都会经历一系列实例化步骤,例如,须要设置数据观察、编译模板、以及建立必要的数据绑定。在这个过程当中,还会调用生命周期钩子
,从而方便咱们执行自定义逻辑,例如,created
钩子会在实例建立后调用:
var vm = new Vue({ data: { a: 1 }, created: function () { // `this` points to the vm instance console.log('a is: ' + this.a) } }) // -> "a is: 1"
还有一些钩子会在实例生命周期的不一样阶段调用,例如compiled、ready
和destroyed
,全部被调用的生命周期钩子经过this
指向调用它的Vue实例,一些用户可能会疑惑在Vue.js的世界中有没有“控制器”的概念,答案是没有。组件的自定义逻辑会被分割到这些生命周期钩子中。
props将数据从父做用域传到子组件。在 Vue.js 中,父子组件的关系能够总结为 props down, events up
。父组件经过 props
向下传递数据给子组件,子组件经过 events
给父组件发送消息。
<div id="app-7"> <ol> <!-- 如今咱们为每一个todo-item提供待办项对象 --> <!-- 待办项对象是变量,即其内容能够是动态的 --> <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item> </ol> </div> Vue.component('todo-item', { props: ['todo'], template: '<li>{{ todo.text }}</li>' }) var app7 = new Vue({ el: '#app-7', data: { groceryList: [ { text: '蔬菜' }, { text: '奶酪' }, { text: '随便其余什么人吃的东西' } ] } })
这只是一个假设的例子,可是咱们已经设法将应用分割成了两个更小的单元,子单元经过 props
接口实现了与父单元很好的解耦。咱们如今能够进一步为咱们的 todo-item
组件实现更复杂的模板和逻辑的改进,而不会影响到父单元。
组件实例的做用域是孤立的。这意味着不能(也不该该)在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,咱们须要经过子组件的
props
选项。
子组件要显式地用props
选项声明它期待得到的数据:
Vue.component('child', { // 声明 props props: ['message'], // 就像 data 同样,prop 能够用在模板内 // 一样也能够在 vm 实例中像 “this.message” 这样使用 template: '<span>{{ message }}</span>' })
而后咱们能够这样向它传入一个普通字符串:
<child message="hello!"></child>
结果:
hello!
咱们先来看一下这个比较经典的问题,当初在学Vue的时候也犯过这样的迷惑,不知道什么时候传递data对象,什么时候传递data函数 。Vue.js的data是要一个对象仍是一个function?
Vue 实例的数据对象。Vue.js 会递归地将它所有属性转为 getter/setter,从而让它能响应数据变化。这个对象必须是普通对象:原生对象,getter/setter 及原型属性会被忽略。不推荐观察复杂对象。
在实例建立以后,能够用 vm.$data 访问原始数据对象。Vue 实例也代理了数据对象全部的属性。
在定义组件
时,同必定义将建立多个实例,此时 data
必须是一个函数,返回原始数据对象。若是 data 仍然是一个普通对象,则全部的实例将指向同一个对象!换成函数后,每当建立一个实例时,会调用这个函数,返回一个新的原始数据对象的副本。
简单说, 在实例中data是对象, 在组件中data就得是函数返回对象。
组件中的data写法示例:
<div id="example-2"> <simple-counter></simple-counter> </div> var data = { counter: 0 } Vue.component('simple-counter', { template: '<button v-on:click="counter += 1">{{ counter }}</button>', // 技术上 data 的确是一个函数了,所以 Vue 不会警告, // 可是咱们返回给每一个组件的实例的却引用了同一个data对象 data: function () { return data } }) new Vue({ el: '#example-2' })
因为这三个组件共享了同一个 data
, 所以增长一个 counter
会影响全部组件!这不对。咱们能够经过为每一个组件返回全新的 data
对象来解决这个问题:
data: function () { return { counter: 0 } }
更多详情请参考:官网组件数据data传递说明(Component)
一个组件下只能有一个并列的 div
,能够这么写,因此复制官网示例的时候只要复制 div
里面的内容就好。
可是不能这样写:
第二。数据要写在 return 里面而不是像文档那样子写
错误的写法:
组件使用 :firstComponent.vue
<template> <div id="firstcomponent"> <h1>I am a title.</h1> <a> written by {{ author }} </a> </div> </template> <script type="text/javascript"> export default { data () { return { author: "微信公众号 jinkey-love" } } } </script> <style> </style>
其余Vue问题,能够参考该博文:Vue2.0 新手彻底填坑攻略——从环境搭建到发布
在了解了vue的基本用法(数据绑定、指令、缩写、条件渲染等)后,来看一个完整示例,动态添加/删除元素:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" href="styles/demo.css" /> </head> <body> <div id="app"> <fieldset> <legend> Create New Person </legend> <div class="form-group"> <label>Name:</label> <input type="text" v-model="newPerson.name"/> </div> <div class="form-group"> <label>Age:</label> <input type="text" v-model="newPerson.age"/> </div> <div class="form-group"> <label>Sex:</label> <select v-model="newPerson.sex"> <option value="Male">Male</option> <option value="Female">Female</option> </select> </div> <div class="form-group"> <label></label> <button @click="createPerson">Create</button> </div> </fieldset> <table> <thead> <tr> <th>Name</th> <th>Age</th> <th>Sex</th> <th>Delete</th> </tr> </thead> <tbody> <tr v-for="person in people"> <td>{{ person.name }}</td> <td>{{ person.age }}</td> <td>{{ person.sex }}</td> <td :class="'text-center'"><button @click="deletePerson($index)">Delete</button></td> <td><button @click="greet">Greet</button></td> </tr> </tbody> </table> </div> </body> <script src="js/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { newPerson: { name: '', age: 0, sex: 'Male' }, people: [{ name: 'Jack', age: 30, sex: 'Male' }, { name: 'Bill', age: 26, sex: 'Male' }, { name: 'Tracy', age: 22, sex: 'Female' }, { name: 'Chris', age: 36, sex: 'Male' }] }, methods:{ createPerson: function(){ this.people.push(this.newPerson); // 添加完newPerson对象后,重置newPerson对象 this.newPerson = {name: '', age: 0, sex: 'Male'} }, deletePerson: function(index){ // 删一个数组元素 this.people.splice(index,1); alert(index); }, greet:function(){ alert(this.newPerson.sex); } } }) </script> </html>
添加、删除表单数据
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>Vue js</title> <link rel="stylesheet" type="text/css" href="../assets/css/bootstrap.min.css"> </head> <body> <header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> <h1>Vue开发</h1> </div> </header> <div class="container" id="app" style="margin:100px;"> <div class="row"> <div class="col-md-offset-2 col-md-8"> <div class="panel panel-default"> <div class="panel-heading"> welcome to Vue.js </div> <div class="panel-body"> <h1>{{ message }}</h1> <!-- <input type="text" class="form-control" v-model="message" /> --> <ul class="list-group"> <li class="list-group-item" v-for="(todo,index) in todos"> {{ todo.id }} {{ todo.title }} <button class="btn btn-danger btn-sm pull-right" v-on:click="deleteTodo(index)" >删除</button> </li> </ul> <form v-on:submit.prevent="addTodo(newTodo)"> <div class="form-group"> <input type="text" v-model="newTodo.title" class="form-control" placeholder="Add a list" /> </div> <div class="form-group"> <button class="btn btn-success">Add to do</button> </div> </form> </div> </div> </div> </div> </div> </body> <!-- 注意:此处的引入文件须要放在body的后边,须要DOM加载完后,才能获取到#app--> <script src="../assets/js/vue.js"></script> <script> new Vue({ el:'#app', data:{ message:"Hello,Vue", todos:[ {id:1,title:'Jack Chan'}, {id:2,title:'Jet Lee'} ], newTodo:{id:null,title:""} }, methods:{ addTodo(newTodo){ this.todos.push(newTodo) this.newTodo = {id:null,title:""} }, deleteTodo(index){ this.todos.splice(index,1) } } }) </script> </html>
注意:在输入框输入数据添加到列表时,须要使用v-on:submit.prevent="addTodo(newTodo)
方法对表单提交进行阻止,并使用v-model
进行数据双向绑定,当输入框里的数据变化时,Vue实例中的newTodo:{id:null,title:""}
属性数据也跟着变化,这样就能够将数据塞入到todos数组中。
<!-- 模板再也不简单和清晰,因此,这里引入computed,增长代码可读性 <h1>My todos {{todos.length}}</h1> --> <h1>My todos {{todosCount}}</h1>
JS中的用法:
computed:{ todosCount(){ return this.todos.length; }
完整代码:
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>Vue js</title> <link rel="stylesheet" type="text/css" href="../assets/css/bootstrap.min.css"> <style> .completed{ color:#5c5b5c; text-decoration: line-through; } </style> </head> <body> <header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> <h1>Vue开发</h1> </div> </header> <div class="container" id="app" style="margin:100px;"> <div class="row"> <div class="col-md-offset-2 col-md-8"> <div class="panel panel-default"> <div class="panel-heading"> welcome to Vue.js </div> <div class="panel-body"> <!-- 模板再也不简单和清晰,因此,这里引入computed,增长代码可读性 <h1>My todos {{todos.length}}</h1> --> <h1>My todos {{todosCount}}</h1> <ul class="list-group"> <li class="list-group-item" v-bind:class="{ 'completed' : todo.completed }" v-for="(todo,index) in todos"> {{ todo.id }} {{ todo.title }} <button class="btn btn-warning btn-xs pull-right" v-bind:class="[todo.completed ? 'btn-danger' : 'btn-success']" v-on:click="toggleCompletion(todo)" > {{ todo.completed ? 'undo' : 'complete' }} </button> <button class="btn btn-danger btn-xs pull-right" v-on:click="deleteTodo(index)" >删除</button> </li> </ul> <form v-on:submit.prevent="addTodo(newTodo)"> <div class="form-group"> <input type="text" v-model="newTodo.title" class="form-control" placeholder="Add a list" /> </div> <div class="form-group"> <button class="btn btn-success">Add to do</button> </div> </form> </div> </div> </div> </div> </div> </body> <!-- 注意:此处的引入文件须要放在body的后边,须要DOM加载完后,才能获取到#app--> <script src="../assets/js/vue.js"></script> <script> new Vue({ el:'#app', data:{ message:"Hello,Vue", todos:[ {id:1,title:'Jack Chan',completed:true}, {id:2,title:'Jet Lee',completed:false} ], newTodo:{id:null,title:""} }, computed:{ todosCount(){ return this.todos.length; } }, methods:{ addTodo(newTodo){ this.todos.push(newTodo) this.newTodo = {id:null,title:""} }, deleteTodo(index){ this.todos.splice(index,1) }, toggleCompletion(todo){ todo.completed = !todo.completed; } } }) </script> </html>
组件(Component)是 Vue.js 最强大的功能之一。组件能够扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些状况下,组件也能够是原生 HTML 元素的形式,以 is 特性扩展。
组件化的好处:
增长了代码的可读性,更重要的是增长了代码的可重用性。
先注册,而后再使用
<div id="example"> <my-component></my-component> </div> // 注册 Vue.component('my-component', { template: '<div>A custom component!</div>' }) // 建立根实例 new Vue({ el: '#example' })
渲染:
<div id="example"> <div>A custom component!</div> </div>
注意事项:组件在注册以后,即可以在父实例的模块中以自定义元素 <my-component></my-component> 的形式使用。要确保在初始化根实例
以前
注册了组件。
没必要在全局注册每一个组件。经过使用组件实例选项注册,可使组件仅在另外一个实例/组件的做用域中可用:
var Child = { template: '<div>A custom component!</div>' } new Vue({ // ... components: { // <my-component> 将只在父模板可用 'my-component': Child } })
对上边的实战示例进行组件化封装处理:
本案例,完成两个组件化工做:
1:对列表进行组件封装,注意在组件模板属性参数的v-bind:todos="todos"
传递
2:对表单进行了组装,注意data为function返回
注意在
JavaScript
中对象和数组是引用类型
,指向同一个内存空间,若是 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
<!DOCTYPE html> <!-- 组件化的好处: 增长了代码的可读性,更重要的是增长了代码的可重用性。 本案例,完成两个组件化工做: 1:对列表进行组件封装,注意在组件模板属性参数的v-bind:todos="todos"传递 2:对表单进行了组装,注意data为function返回 注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,若是 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。 --> <head> <meta charset="UTF-8"> <title>Vue js</title> <link rel="stylesheet" type="text/css" href="../assets/css/bootstrap.min.css"> <style> .completed{ color:#5c5b5c; text-decoration: line-through; } </style> </head> <body> <header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> <h1>Vue开发</h1> </div> </header> <div class="container" id="app" style="margin:100px;"> <div class="row"> <div class="col-md-offset-2 col-md-8"> <div class="panel panel-default"> <div class="panel-heading"> welcome to Vue.js </div> <div class="panel-body"> <!-- 模板再也不简单和清晰,因此,这里引入computed,增长代码可读性 <h1>My todos {{todos.length}}</h1> --> <h1>My todos {{todosCount}}</h1> <!-- 这里使用组件化封装ul列表,这里须要给组件绑定一个数据属性,v-bind: 能够简写为一个冒号:--> <todo-items v-bind:todos="todos"></todo-items> <todo-form :todos="todos"></todo-form> </div> </div> </div> </div> </div> </body> <!-- 列表组件 --> <script type="text/x-template" id="todo-items-template"> <ul class="list-group"> <li class="list-group-item" v-bind:class="{ 'completed' : todo.completed }" v-for="(todo,index) in todos"> {{ todo.id }} {{ todo.title }} <button class="btn btn-warning btn-xs pull-right" v-bind:class="[todo.completed ? 'btn-danger' : 'btn-success']" v-on:click="toggleCompletion(todo)" > {{ todo.completed ? 'undo' : 'complete' }} </button> <button class="btn btn-danger btn-xs pull-right" v-on:click="deleteTodo(index)" >删除</button> </li> </ul> </script> <!-- 表单组件 --> <script type="text/x-template" id="add-form-template"> <form v-on:submit.prevent="addTodo(newTodo)"> <div class="form-group"> <input type="text" v-model="newTodo.title" class="form-control" placeholder="Add a list" /> </div> <div class="form-group"> <button class="btn btn-success">Add to do</button> </div> </form> </script> <!-- 注意:此处的引入文件须要放在body的后边,须要DOM加载完后,才能获取到#app--> <script src="../assets/js/vue.js"></script> <script> // 组件化-列表 Vue.component('todo-items',{ template:'#todo-items-template', props:['todos'], // 定义一个属性 methods:{ deleteTodo(index){ this.todos.splice(index,1) }, toggleCompletion(todo){ todo.completed = !todo.completed; } } }) // 组件化-表单 Vue.component('todo-form',{ template:'#add-form-template', props:['todos'], // data为function返回 data(){ return{ newTodo:{id:null,title:"",completed:false} } }, methods:{ addTodo(newTodo){ this.todos.push(newTodo) this.newTodo = {id:null,title:"",completed:false} } } }) new Vue({ el:'#app', data:{ message:"Hello,Vue", todos:[ {id:1,title:'Jack Chan',completed:true}, {id:2,title:'Jet Lee',completed:false} ], }, computed:{ todosCount(){ return this.todos.length; } } }) </script> </html>
vue-cli脚手架Github地址:https://github.com/vuejs/vue-cli
// 这里使用淘宝的镜像cnpm $ cnpm install -g vue-cli
安装好以后,能够经过vue
命令查看:
➜ Code vue init webpack vuejs-cli
而后再执行上边给出的提示命令:
cd vuejs-2.0-cli npm install npm run dev
执行完上边的命令后,会打开浏览器的http://localhost:8080/#/页面
咱们看一下这个目录下边的文件:
vue-router
是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超连接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。
<div class="list-group"> <a class="list-group-item" v-link="{ path: '/home'}">Home</a> <a class="list-group-item" v-link="{ path: '/about'}">About</a> </div>
相信不少人都使用过export、export default、import,然而它们到底有什么区别呢? 在JavaScript ES6中,export与export default都可用于导出常量、函数、文件、模块等,你能够在其它文件或模块中经过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便可以对其进行使用,但在一个文件或模块中,export、import能够有多个,export default仅有一个。
具体事例,请看下边的原文章:
JavaScript ES6中export及export default的区别
axios github官方地址
vue-axios 扩展包GitHub地址
咱们在学习vue的API请求,因此,咱们用第二个特定的包vue-axios
来安装https://github.com/imcvampire...。
npm install --save axios vue-axios
安装好以后,引入到使用的文件中
import Vue from 'vue' import axios from 'axios' import VueAxios from 'vue-axios' Vue.use(VueAxios, axios)
示例:
Vue.axios.get(api).then((response) => { console.log(response.data) }) this.axios.get(api).then((response) => { console.log(response.data) })
这里使用axios
请求接口,会出现跨域的问题,不过,咱们能够经过安装https://github.com/barryvdh/laravel-cors
库来解决这个问题。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。---官方文档
state
这样概念初次接触的时候可能会感受到有点模糊,简单来讲就是将 state 当作咱们项目中使用的数据的集合。而后,Vuex 使得 组件本地状态(component local state)
和 应用层级状态(application state)
有了必定的差别。
component local state:该状态表示仅仅在组件内部使用的状态,有点相似经过配置选项传入 Vue 组件内部的意思。
application level state:应用层级状态,表示同时被多个组件共享的状态层级。
假设有这样一个场景:咱们有一个父组件,同时包含两个子组件。父组件能够很容易的经过使用 props 属性来向子组件传递数据。
可是问题来了,当咱们的两个子组件如何和对方互相通讯的? 或者子组件如何传递数据给他父组件的?在咱们的项目很小的时候,这个两个问题都不会太难,由于咱们能够经过事件派发和监听来完成父组件和子组件的通讯。
然而,随着咱们项目的增加:
保持对全部的事件追踪将变得很困难。到底哪一个事件是哪一个组件派发的,哪一个组件该监听哪一个事件?
项目逻辑分散在各个组件当中,很容易致使逻辑的混乱,不利于咱们项目的维护。
父组件将变得和子组件耦合愈来愈严重,由于它须要明确的派发和监听子组件的某些事件。
这就是 Vuex 用来解决的问题。 Vuex 的四个核心概念分别是:
The state tree:Vuex 使用单一状态树,用一个对象就包含了所有的应用层级状态。至此它便做为一个『惟一数据源(SSOT)』而存在。这也意味着,每一个应用将仅仅包含一个 store 实例。单状态树让咱们可以直接地定位任一特定的状态片断,在调试的过程当中也能轻易地取得整个当前应用状态的快照。
Getters:用来从 store 获取 Vue 组件数据。
Mutators:事件处理器用来驱动状态的变化。
Actions:能够给组件使用的函数,以此用来驱动事件处理器 mutations
如何你暂时还不太理解这个四个概念,不用着急,咱们将在后面的项目实战中详细的解释。
Vuex 应用中数据的流向(Vuex 官方图)
上边的流程图简单解释下:
Vuex 规定,属于应用层级的状态只能经过 Mutation 中的方法来修改,而派发 Mutation 中的事件只能经过 action。
从左到又,从组件出发,组件中调用 action,在 action 这一层级咱们能够和后台数据交互,好比获取初始化的数据源,或者中间数据的过滤等。而后在 action 中去派发 Mutation。Mutation 去触发状态的改变,状态的改变,将触发视图的更新。
注意事项
数据流都是单向的
组件可以调用 action
action 用来派发 Mutation
只有 mutation 能够改变状态
store 是响应式的,不管 state 何时更新,组件都将同步更新
咱们来看一下建立的Vuex项目的目录结构:
components/ 文件夹用来存放咱们的 Vue 组件
vuex/ 文件夹存放的是和 Vuex store 相关的东西(state object,actions,mutators)
build/ 文件是 webpack 的打包编译配置文件
config/ 文件夹存放的是一些配置项,好比咱们服务器访问的端口配置等
dist/ 该文件夹一开始是不存在,在咱们的项目通过 build 以后才会产出
App.vue 根组件,全部的子组件都将在这里被引用
index.html 整个项目的入口文件,将会引用咱们的根组件 App.vue
main.js 入口文件的 js 逻辑,在 webpack 打包以后将被注入到 index.html 中
注:本博客Vuex部份内容转自该博文:使用 Vuex + Vue.js 构建单页应用,博文做者对Vuex理解的比较透彻,因此转过来学习下。
先看Vuex的目录结构:
主要是在 src
目录下作组件重构:
先看列表组件Todos.vue
:
<template> <div id="todos"> <ul class="list-group"> <li class="list-group-item" v-bind:class="{ 'completed' : todo.completed }" v-for="(todo,index) in todos"> {{ todo.id }} <router-link :to="{ name: 'todo', params: { id: todo.id }}">{{ todo.title }}</router-link> <button class="btn btn-warning btn-xs pull-right margin-right-10" v-bind:class="[todo.completed ? 'btn-danger' : 'btn-success']" v-on:click="toggleCompletion(todo)" > {{ todo.completed ? 'undo' : 'complete' }} </button> <button class="btn btn-danger btn-xs pull-right margin-right-10" v-on:click="deleteTodo(todo, index)" >删除</button> </li> </ul> <todo-form></todo-form> </div> </template> <style> .completed{ color:#5c5b5c; text-decoration: line-through; } .margin-right-10{ margin-right: 10px; } </style> <script> import TodoForm from './TodoForm'; export default{ name:'todos', // props:['todos'], // 定义一个属性 computed: { todos() { return this.$store.state.todos } }, methods:{ deleteTodo(todo, index){ this.$store.dispatch('removeTodo', todo, index) }, toggleCompletion(todo){ this.$store.dispatch('completeTodo', todo) } }, components:{ TodoForm } } </script>
表单组件TodoForm.vue
<template> <form v-on:submit.prevent="addTodo(newTodo)"> <div class="form-group"> <input type="text" v-model="newTodo.title" class="form-control" placeholder="Add a list" /> </div> <div class="form-group"> <button class="btn btn-success" type="submit">Add to do</button> </div> </form> </template> <style> .completed{ color:#5c5b5c; text-decoration: line-through; } .margin-right-10{ margin-right: 10px; } </style> <script> export default{ // props:['todos'], // 子组件数据属性 /* Vuex 写法,将 newTodo 放入到 state 中 data(){ return{ newTodo:{id:null,title:"",completed:false} } },*/ computed: { newTodo() { return this.$store.state.newTodo } }, methods:{ addTodo(newTodo){ // this.todos.push(newTodo) // this.newTodo = {id:null,title:"",completed:false} this.$store.dispatch('saveTodo', newTodo) } } } </script>
App.vue
总组件:
<template> <div id="app"> <img src="./assets/logo.png"> <!-- <todos :todos="todos"></todos> --> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div> </template> <script> import Hello from './components/Hello'; import Todos from './components/Todos'; export default { name: 'app', /* data(){ return { todos:[ {id:1,title:'Jack Chan',completed:true}, {id:2,title:'Jet Lee',completed:false} ] } }, */ // 异步请求 mounted(){ /* this.axios.get('http://baidu.com').then(response =>{ console.log(response.data) })*/ this.$store.dispatch('getTodos') }, computed:{ todosCount(){ return this.$store.todos.length; } }, components: { Hello } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
main.js
文件:
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' // 引入axios请求API import axios from 'axios' import VueAxios from 'vue-axios' Vue.use(VueAxios, axios) // 引入路由 import VueRouter from 'vue-router' Vue.use(VueRouter) // 引入vuex import Vuex from 'vuex' Vue.use(Vuex) import Todos from './components/Todos'; import Todo from './components/Todo'; const routes = [ { path: '/', component: Todos }, { path: '/todo/:id', component: Todo, name:'todo'} ] const router = new VueRouter({ routes // (缩写)至关于 routes: routes }) /* ====================== Vuex说明 ==================== // store:能够理解为一个大的容器 state: 至关于一个全局变量数据属性 mutations:里边的方法专门操做state里边的全局变量 actions: 模型组件操做actions,获取数据,而后触发mutations 小结:Vuex的核心,主要是理解其应用的场景,若是理清了这些思路,就能够很容易进行开发了。 另外咱们也能够将store这变量拆分为一个store.js文件,而后引用进来便可。 ====================================================== */ // Vuex example // store 能够理解为一个大的容器 const store = new Vuex.Store({ state: { // state 能够理解为全局的变量,能够在任意组件中使用 todos: [], newTodo:{id:null, title:"", completed:false} }, // mutations里边的方法用来修改state里的数据 mutations: { get_todo_list(state, todos) { state.todos = todos; }, complete_todo(state, todo){ todo.completed = ! todo.completed }, delete_todo(state, index){ state.todos.splice(index, 1) }, add_todo(state, todo){ state.todos.push(todo) } }, // actions 主要用来获取客户端的数据,而后将数据传给 mutations 的方法 actions:{ getTodos(store){ store.commit('get_todo_list', [{id : 1, title:"hello,world!"}]) }, completeTodo(state, todo){ store.commit('complete_todo', todo) }, removeTodo(store, todo, index){ store.commit('delete_todo', index) }, saveTodo(store, todo){ store.commit('add_todo', todo) store.state.newTodo = {id:null,title:"",completed:false} }, } }) /* eslint-disable no-new */ new Vue({ el: '#app', store, template: '<App/>', components: { App }, router })
主页面 index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>vuejs-2.0-cli</title> <link rel="stylesheet" href="https://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css"> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
本项目GitHub地址:
https://github.com/corwien/vu...
更多有关Vue.js学习的文章,请看这里:
Vue.js——60分钟快速入门
Vue.js——基于$.ajax实现数据的跨域增删查改
Vuejs2.0 文档攻略-介绍
使用 Vuex + Vue.js 构建单页应用
使用Vue.js和Vuex实现购物车场景
Vuex学习