1.Vue基础部分
属性计算 computed
- 模板内的表达式不应该包含太多的逻辑,对于任何复杂逻辑,都应当使用计算属性
- computed属性和methods不同的是计算属性是基于它们的依赖进行缓存的
- computed属性和watch属性,通常更好的想法是使用computed属性而不是命令式的watch回调。虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的watcher。当想要在数据变化响应时,执行异步操作或开销较大的操作,watch更适合
- 计算属性默认只拥有getter方法,但是可以自定义一个setter方法
computed: {
fullName: {
// getter
get: function () {
return this.firstName + " " + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(" ")
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
//下面语句触发setter方法,firstName和lastName也会被相应更新
vm.fullName = "John Doe"
观察者属性watch
通过watch属性可以手动观察Vue实例上的数据变动,当然也可以调用实例上的vm.$watch达到相同的目的
<div id="watch-example">
<p>Ask a yes/no question: <input v-model="question"></p>
<p>{{ answer }}</p>
</div>
<script>
var watchExampleVM = new Vue({
el: "#watch-example",
data: {
question: "",
answer: "I cannot give you an answer until you ask a question!"
},
watch: {
// 如果question发生改变,该函数就会运行
question: function (newQuestion) {
this.answer = "Waiting for you to stop typing..."
this.getAnswer()
}
},
methods: {
// _.debounce是lodash当中限制操作频率的函数
getAnswer: _.debounce(
function () {
if (this.question.indexOf("?") === -1) {
this.answer = "Questions usually contain a question mark. ;-)"
return
}
this.answer = "Thinking..."
var vm = this
axios.get("https://yesno.wtf/api")
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = "Error! Could not reach the API. " + error
})
},
// 这是用户停止输入等待的毫秒数
500
)
}
})
</script>
Vue实例暴露了一系列带有前缀$的实例属性与方法
let vm = new Vue();
vm = {
// Vue实例属性的代理
$data: "被watch的data对象",
$props: "当前组件收到的props",
$el: "Vue实例使用的根DOM元素",
$options: "当前Vue实例的初始化选项",
$parent: "父组件Vue对象的实例",
$root: "根组件Vue对象的实例",
$children: "当前实例的直接子组件",
$slots: "访问被slot分发的内容",
$scopedSlots: "访问scoped slots",
$refs: "包含所有拥有ref注册的子组件",
$isServer: "判断Vue实例是否运行于服务器",
$attrs: "包含父作用域中非props的属性绑定",
$listeners: "包含了父作用域中的v-on事件监听器",
// 数据
$watch: "观察Vue实例变化的表达式、计算属性函数",
$set: "全局Vue.set的别名",
$delete: "全局Vue.delete的别名",
// 事件
$on: "监听当前实例上的自定义事件,事件可以由vm.$emit触发",
$once: "监听一个自定义事件,触发一次之后就移除监听器",
$off: "移除自定义事件监听器",
$emit: "触发当前实例上的事件",
// 生命周期
$mount: "手动地挂载一个没有挂载的Vue实例",
$forceUpdate: "强制Vue实例重新渲染,仅影响实例本身和插入插槽内容的子组件",
$nextTick: "将回调延迟到下次DOM更新循环之后执行",
$destroy: "完全销毁一个实例",
}
$refs属性
子组件指定ref属性之后,可以通过父组件的$refs实例属性对其进行访问
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
<script>
var parent = new Vue({ el: "#parent" })
var child = parent.$refs.profile // 访问子组件
</script>
组件可以扩展HTML元素功能,并且封装可重用代码。可以通过Vue.component( id, [definition] )注册或者获取全局组件
// 注册组件,传入一个扩展过的构造器
Vue.component("my-component", Vue.extend({ ... }))
// 注册组件,传入一个option对象(会自动调用Vue.extend)
Vue.component("my-component", { ... })
// 获取注册的组件(始终返回构造器)
var MyComponent = Vue.component("my-component")
组件的生命周期
- 不要在Vue实例的属性和回调上使用箭头函数,比如created: () => console.log(this.a)或vm.$watch(“a”, newValue => this.myMethod())。因为箭头函数的this与父级上下文绑定,并不指向Vue实例本身,所以前面代码中的this.a或this.myMethod将会是undefined
通过jQuery对DOM进行的操作可以放置在Mounted属性上进行,即当Vue组件已经完成在DOM上挂载的时候
2.Vue-Router
router-link和router-view 组件
router-link组件: 其实是封装了一个a 标签(链接标签),里面有一个重要的属性to,它指定的一个路径,跳转的路径.
router-view组件:视图渲染组件,通过router-link跳转到的页面,加载的组件,都会在router-view组件里显示(渲 染)出来。
需要注意的几点:首先,创建一个路由对象.
{ path: '/user/:name', component: () => import('@/views/user.vue') }
那么,这个name 就是一个动态路由参数
- 在views 里创建 user.vue
```javascript
I am pika, I am {{ $route.params.name }}
- 改变url , 对应的 $route.params.name ,这个动态的,路由的参数,也会改变 - $route : 代表当前加载页面的路由对象,它是一个对象,里面包含一个params属性,params对象里面又包含了一个name 的值,它是一个动态的值(它是一个形参类型的值,随url 的改变而改变). - 只需要传递不同的参数,加载同一个页面,加载不同的逻辑. <a name="bRNeU"></a> #### b.嵌套路由 - 多层嵌套组件: 可以通过嵌套路由,来渲染. - 在路由列表中添加parent路由对象 ```javascript { path: '/parent', component: () => import('@/views/Parent.vue'), children: [ { path: 'child', component: () => import('@/views/Child.vue') } ] }
注意:
3.Vuex
Vuex是专门为Vue应用程序提供的状态管理模式,每个Vuex应用的核心是store(仓库),即装载应用程序state(状态)的容器,每个应用通常只拥有一个store实例。
Vuex的state是响应式的,即store中的state发生变化时,相应组件也会进行更新,修改store当中state的唯一途径是提交mutations。const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } }) store.commit("increment") // 通过store.state来获取状态对象 console.log(store.state.count) // 通过store.commit()改变状态
State
从store当中获取state的最简单办法是在计算属性中返回指定的state,每当state发生改变的时候都会重新执行计算属性,并且更新关联的DOM
const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return store.state.count } } }
- Vuex提供store选项,将state从根组件注入到每个子组件中,从而避免频繁import store。
// 父组件中注册store属性 const app = new Vue({ el: "#app", store: store, components: { Counter }, template: ` <div class="app"> <counter></counter> </div>` }) // 子组件,store会注入到子组件,子组件可通过this.$store进行访问 const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return this.$store.state.count } } }
- Vuex提供mapState()辅助函数,避免使用多个state的场景下,多次去声明计算属性
// 在单独构建的版本中辅助函数为 Vuex.mapState import { mapState } from "vuex" export default { computed: mapState({ count: state => state.count, // 传递字符串参数"count"等同于`state => state.count` countAlias: "count", countPlusLocalState (state) { return state.count + this.localCount } }) } // 当计算属性名称与state子节点名称相同时,可以向mapState传递一个字符串数组 computed: mapState([ "count" // 映射this.count到store.state.count ])
- mapState()函数返回一个包含有state相关计算属性的对象,这里可以通过ES6的对象展开运算符…将该对象与Vue组件本身的computed属性进行合并
computed: { localComputed () {}, ...mapState({}) }
- Vuex允许在store中定义getters(可视为store的计算属性),getters的返回值会根据其依赖被缓存,只有当依赖值发生了改变才会被重新计算。该方法接收state作为第1个参数,其它getters作为第2个参数。可以直接在store上调用getters来获取指定的计算值
const store = new Vuex.Store({ state: { todos: [ { id: 1, text: "...", done: true }, { id: 2, text: "...", done: false } ] }, getters: { doneTodos: (state, getters) => { return state.todos.filter(todo => todo.done) } } }) // 获取doneTodos = [{ id: 1, text: "...", done: true }] store.getters.doneTodos
这样就可以方便的根据store中现有的state派生出新的state,从而避免在多个组件中复用时造成代码冗余
computed: { doneTodosCount () { return this.$store.getters.doneTodos // 现在可以方便的在Vue组件使用store中定义的doneTodos } }
- Vuex提供的mapGetters()辅助函数将store中的getters映射到局部计算属性
import { mapGetters } from "vuex" export default { computed: { // 使用对象展开运算符将getters混入computed计算属性 ...mapGetters([ "doneTodosCount", doneCount: "doneTodosCount" // 映射store.getters.doneTodosCount到别名this.doneCount ]) } }
Mutations
修改store中的state的唯一方法是提交mutations,mutations类似于自定义事件,拥有一个字符串事件类型和一个回调函数(接收state作为参数,是对state进行修改的位置)
const store = new Vuex.Store({ state: { count: 1 }, mutations: { // 触发类型为increment的mutation时被调用 increment (state) { state.count++ // 变更状态 } } }) // 触发mutation store.commit("increment")
- 可以通过store的commit()方法触发指定的mutations,也可以通过store.commit()向mutation传递参数
// commit() store.commit({ type: "increment", amount: 10 }) // store mutations: { increment (state, payload) { state.count += payload.amount } }
mutation事件类型建议使用常量,并且将这些常量放置在单独文件,便于管理和防止重复
// mutation-types.js export const SOME_MUTATION = "SOME_MUTATION" // store.js import Vuex from "vuex" import { SOME_MUTATION } from "./mutation-types" const store = new Vuex.Store({ state: { ... }, mutations: { // 可以通过ES6的计算属性命名特性去使用常量作为函数名 [SOME_MUTATION] (state) { // mutate state } } }) <!--mutation()必须是同步函数,因为devtool无法追踪回调函数中对state进行的异步修改--> <!---->
- Vue组件可以使用this.$store.commit(“xxx”)提交mutation,或者使用mapMutations()将Vue组件中的methods映射为store.commit调用(需要在根节点注入store)
import { mapMutations } from "vuex" export default { methods: { ...mapMutations([ "increment" // 映射this.increment()为this.$store.commit("increment") ]), ...mapMutations({ add: "increment" // 映射this.add()为this.$store.commit("increment") }) } }
Actions
Action用来提交mutation,且Action中可以包含异步操作。Action函数接受一个与store实例具有相同方法和属性的context对象,因此可以通过调用context.commit提交一个mutation,或者通过context.state和context.getters来获取state、getters
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit("increment") } } })
- 组件中可以使用this.$store.dispatch(“xxx”)分发action,或者使用mapActions()将组件的methods映射为store.dispatch(需要在根节点注入store)
import { mapActions } from "vuex" export default { methods: { ...mapActions([ "increment" // 映射this.increment()为this.$store.dispatch("increment") ]), ...mapActions({ add: "increment" // 映射this.add()为this.$store.dispatch("increment") }) } }
store.dispatch可以处理action回调函数当中返回的Promise,并且store.dispatch本身仍然返回一个Promise
actions: { // 定义一个返回Promise对象的actionA actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit("someMutation") // 触发mutation resolve() }, 1000) }) }, // 也可以在actionB中分发actionA actionB ({ dispatch, commit }) { return dispatch("actionA").then(() => { commit("someOtherMutation") // 触发另外一个mutation }) } } // 现在可以分发actionA store.dispatch("actionA").then(() => { ... ... ... })
可以体验通过ES7的异步处理特性async/await来组合action
actions: { async actionA ({ commit }) { commit("gotData", await getData()) }, async actionB ({ dispatch, commit }) { await dispatch("actionA") //等待actionA完成 commit("gotOtherData", await getOtherData()) } }
Module
整个应用使用单一状态树的情况下,所有state都会集中到一个store对象,因此store可能变得非常臃肿。因此,Vuex允许将store切割成模块(module),每个模块拥有自己的state、mutation、action、getter、甚至是嵌套的子模块
const moduleA = { state: {}, mutations: {}, actions: {}, getters: {} } const moduleB = { state: {}, mutations: {}, actions: {} } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // moduleA的状态 store.state.b // moduleB的状态
- module内部的mutations()和getters()接收的第1个参数是模块的局部状态对象
const moduleA = { state: { count: 0 }, mutations: { increment (state) { state.count++ // 这里的state是模块的局部状态 } }, getters: { doubleCount (state) { return state.count * 2 } } }
- 模块内部action当中,可以通过context.state获取局部状态,以及context.rootState获取全局状态
const moduleA = { // ... actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit("increment") } } } }
- 模块内部的getters()方法,可以通过其第3个参数接收到全局状态
const moduleA = { getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } } }
4.构建打包与部署
打包、部署
部署就是把代码放到各种不同的环境下运行,因为我们在本地,测试环境,生产环境所需要的资源跟条件都不一样,比如你的是mac系统,他的是windows系统,可是都要运行该程序,那么需要通过 yarn build 或者 npm run build 来将代码进行整理、编译之后得到的dist文件放在同一个服务器上面,让不同的用户进行访问。
webSever
搭建webserver的方式有很多种: