Fork me on GitHub

随笔分类 - vue

vue笔记12-状态管理vuex

[TOC] [官方文档](https://vuex.vuejs.org/zh-cn/) # 1. State vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。 ## 1.1. 最简单的获取store实例中状态的方法 ``` // 创建一个 Counter 组件,在computed中返回。 const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { //vuex的状态存储是响应式的 return store.state.count } } } ``` ## 1.2. mapState辅助函数 我们可以使用 mapState 辅助函数帮助我们生成计算属性,将组件中的**computed属性**映射为 **store 中的 state** ``` // 在单独构建的版本中辅助函数为 Vuex.mapState import { mapState } from 'vuex' export default { // ... computed: mapState({ // 箭头函数可使代码更简练 count: state => state.count, // 传字符串参数 'count' 等同于 `state => state.count` countAlias: 'count', // 为了能够使用 `this` 获取局部状态,必须使用常规函数 countPlusLocalState (state) { return state.count + this.localCount } }) } ``` 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。 ``` computed: mapState([ // 映射 this.count 为 store.state.count 'count' ]) ``` # 2. Getter Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 ## 2.1. 基本使用 Getter 接受 state 作为其第一个参数: ``` const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: false } ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) } } }) ``` Getter 会暴露为 store.getters 对象: store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] Getter 也可以接受其他 getter 作为第二个参数: ``` getters: { // ... doneTodosCount: (state, getters) => { return getters.doneTodos.length } } store.getters.doneTodosCount // -> 1 ``` 我们可以很容易地在**任何组件中**使用它: ``` computed: { doneTodosCount () { return this.$store.getters.doneTodosCount } } ``` 你也可以通过让 getter **返回一个函数**,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。 ``` getters: { // ... getTodoById: (state) => (id) => { return state.todos.find(todo => todo.id === id) } } store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false } ``` ## 2.2. mapGetters 辅助函数 我们可以使用 mapGetter 辅助函数帮助我们生成计算属性,将组件中的**computed属性**映射为 **store 中的 getter** ``` import { mapGetters } from 'vuex' export default { // ... computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) } } ``` 如果你想将一个 getter 属性另取一个名字,使用对象形式: ``` mapGetters({ // 映射 `this.doneCount` 为 `store.getters.doneTodosCount` doneCount: 'doneTodosCount' }) ``` # 3. Mutation ## 3.1. 基本使用 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常**类似于事件**:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数: ``` const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 变更状态 state.count++ } } }) ``` 当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法: ``` store.commit('increment') ``` ## 3.2. 提交载荷 ### 3.2.1. 普通提交方式 1.传入额外的参数,即 mutation 的 载荷(payload): ``` // ... mutations: { increment (state, n) { state.count += n } } store.commit('increment', 10) ``` 2.载荷是一个对象 ``` // ... mutations: { increment (state, payload) { state.count += payload.amount } } store.commit('increment', { amount: 10 }) ``` ### 3.2.2. 对象风格的提交方式 提交 mutation 的另一种方式是直接使用包含 type 属性的对象: ``` mutations: { increment (state, payload) { state.count += payload.amount } } store.commit({ type: 'increment', amount: 10 }) ``` ## 3.3. 使用常量替代 Mutation 事件类型 这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 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: { // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名 [SOME_MUTATION] (state) { // mutate state } } }) ``` ## 3.4. Mutation 必须是同步函数 注意:一条重要的原则就是要记住 mutation 必须是同步函数。 ## 3.5. 在组件中提交 Mutation 你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的***methods***映射为 **store.commit** 调用(需要在根节点注入 store)。 ``` import { mapMutations } from 'vuex' export default { // ... methods: { ...mapMutations([ 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` // `mapMutations` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')` }) } } ``` # 4. Action Action 类似于 mutation,不同在于: - Action 提交的是 mutation,而不是直接变更状态。 - Action 可以包含任意异步操作。 ## 4.1 基本用法 让我们来注册一个简单的 action: ``` const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } }) ``` Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。 ## 4.2 分发action Action 通过 store.dispatch 方法触发: store.dispatch('increment') 显然直接分发mutation更方便,但是mutation 必须同步执行,Action 则不受这个限制 我们可以在action内部执行异步操作 ``` actions: { incrementAsync ({ commit }) { setTimeout(() => { commit('increment') }, 1000) } } ``` Actions 支持同样的载荷方式和对象方式进行分发: ``` // 以载荷形式分发 store.dispatch('incrementAsync', { amount: 10 }) // 以对象形式分发 store.dispatch({ type: 'incrementAsync', amount: 10 }) ``` ## 4.3 在组件中分发action 你在组件中使用 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` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)` ]), ...mapActions({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')` }) } } ``` # 5. Module 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。 ## 5.1. 基本使用 为了解决以上问题,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 的状态 ``` ## 5.2 模块的局部状态 对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。 ``` const moduleA = { state: { count: 0 }, mutations: { increment (state) { // 这里的 `state` 对象是模块的局部状态 state.count++ } }, 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') } } } } ``` 对于模块内部的 getter,根节点状态会作为第三个参数暴露出来: ``` const moduleA = { // ... getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } } } ``` # 6 实战分析 [参考官方购物车实例](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart) 以menu为了分析下思路 1.app.js ``` import 'babel-polyfill' import Vue from 'vue' import App from './components/App.vue' import store from './store' //步骤1 import { currency } from './currency' Vue.filter('currency', currency) new Vue({ el: '#app', store, //步骤2 render: h => h(App) }) ``` 做了两步 : 引入store 并绑定到根实例上 2.store/index.js ``` import Vue from 'vue' import Vuex from 'vuex' import * as actions from './actions' import * as getters from './getters' import fruit from './modules/fruit'; Vue.use(Vuex) const store = new Vuex.Store({ actions, getters, modules: { fruit, }, }) export default store; ``` 3.store/nutation-types.js ``` export const FRUIT_SHOW = "FRUIT_SHOW" export const FRUIT_HIDE = "FRUIT_HIDE" ``` 4.store/modules/fruit.js ``` import {FRUIT_SHOW,FRUIT_HIDE,} from '../../store/mutation-types' export default { state: { appearence: false, }, mutations: { [FRUIT_HIDE](state) { state.appearence = false }, [FRUIT_SHOW](state) { state.appearence = true }, }, actions: { hideFruit({commit}) { commit(FRUIT_HIDE) }, showFruit({commit}) { commit(FRUIT_SHOW) }, }, getters: { fruitState:state => state.appearence, } } ``` 5.使用方法 ``` <template> <div> <p v-if="appearence">apple</p> <el-button type="primary" @click="showApple">显示</el-button> <el-button @click="hideApple">隐藏</el-button> </div> </template> ``` ``` <script> import { mapState } from 'vuex'; import { mapGetters } from 'vuex'; export default { data () { return { } }, methods: { showApple(){ //使用commit触发mutations this.$store.commit('FRUIT_SHOW'); //使用dispatch触发actions,action中使用commit触发mutations //this.$store.dispatch('showFruit'); }, hideApple(){ //this.$store.commit('FRUIT_HIDE'); this.$store.dispatch('hideFruit'); }, }, //使用getters computed: mapGetters({ appearence: 'fruitState', }), /** //使用 state computed: mapState({ //appearence: appearence, 这样是错误,少了一层fruit appearence: state => state.fruit.appearence, }), //另一种表示方法 computed:{ ...mapState({ appearence: state => state.fruit.appearence, }) }, **/ mounted() { //使用state获取状态 console.log(this.$store.state.fruit.appearence); //使用getters获取状态 console.log(this.$store.getters.fruitState); }, } </script> ``` 6.另一种定义getters的方法 store/modules/fruit.js ``` import { FRUIT_SHOW, FRUIT_HIDE, } from '../../store/mutation-types' export default { state: { appearence: false, }, mutations: { [FRUIT_HIDE](state) { state.appearence = false }, [FRUIT_SHOW](state) { state.appearence = true }, }, actions: { hideFruit({commit}) { commit(FRUIT_HIDE) }, showFruit({commit}) { commit(FRUIT_SHOW) }, }, //这里不在定义getters } ``` store/getters.js ``` const fruitState = (state) => state.fruit.appearence; export { fruitState, } ```

vue笔记13-vue中使用echarts

# 1.安装 ``` npm install echarts -S ``` # 2.引入 1. 全局引入 main.js中 ``` // 引入echarts import echarts from 'echarts' Vue.prototype.$echarts = echarts ``` 2. 按需引入 全局引入会将所有的echarts图表打包,导致体积过大,最好还是按需引入 ``` let echarts = require('echarts/lib/echarts') require('echarts/lib/chart/pie'); require('echarts/lib/component/grid'); require('echarts/lib/component/legend'); require('echarts/lib/component/tooltip'); require('echarts/lib/component/geo'); require('echarts/lib/component/axisPointer'); require('echarts/lib/component/dataZoom'); require('echarts/lib/component/timeline'); require('echarts/lib/component/toolbox'); require('echarts/lib/component/title'); ``` # 3. 使用 1. 画一个饼图 html ``` <div id="os_chart" style="margin:0 auto;width:750px; height:500px"></div> ``` js ``` data() { return { os_option: { title : { show:false, x:'center' }, legend: { orient: 'vertical', left: 'left', data: [], }, tooltip : { trigger: 'item', formatter: "{a} <br/>{b} : {c} ({d}%)" }, series : [ { name: '操作系统', type: 'pie', radius : '55%', center: ['50%', '60%'], data:[], itemStyle: { emphasis: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ] }, } }, methods: { initChart(){ this.os_chart = echarts.init(document.getElementById('os_chart')) }, initData(){ this.$http.get('/api/statistics/index').then((res) => { if(res.data.success){ this.os_option.legend.data = res.data.data.os.x; this.os_option.series[0].data = res.data.data.os.y; this.os_chart.setOption(this.os_option); } }); }, }, mounted() { this.initChart(); this.initData(); }, ``` 2. 画一个中国地图数据 html ``` <div id="china_map_chart" style="margin:0 auto;width:750px; height:500px"></div> ``` js ``` let echarts = require('echarts/lib/echarts') require('echarts/lib/chart/map'); require('echarts/lib/component/grid'); require('echarts/lib/component/legend'); require('echarts/lib/component/tooltip'); require('echarts/lib/component/geo'); require('echarts/lib/component/axisPointer'); require('echarts/lib/component/dataZoom'); require('echarts/lib/component/timeline'); require('echarts/lib/component/toolbox'); require('echarts/lib/component/title'); //地图数据是自己下载的,按照存放数据的路径引入 import {china} from '../plugins/chart/data/map/china'; import {world} from '../plugins/chart/data/map/world'; export default { data() { return { china_map_option: { title: { show:false, left: 'center', }, tooltip: { trigger: 'item', formatter: function (params) { var value = (params.value + '').split('.'); value = value[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, '$1,'); return params.seriesName + '<br/>' + params.name + ' : ' + value; } }, toolbox: { show: true, orient: 'vertical', left: 'right', top: 'center', feature: { restore: {}, } }, visualMap: { min: 0, max: 38000000, text:['高','低'], range: [0, 38000000], realtime: false, calculable: true, inRange: { color: ['lightskyblue','yellow', 'orangered'] } }, series: [ { name: '国内访问来源统计', type: 'map', mapType: 'china', roam: true, itemStyle:{ emphasis:{label:{show:true}} }, data: [], } ] }, } }, methods: { initChart(){ this.china_map_chart = echarts.init(document.getElementById('china_map_chart')) echarts.registerMap('china', china); }, initData(){ this.$http.get('/api/statistics/index').then((res) => { if(res.data.success){ this.china_map_option.series[0].data = res.data.data.china; this.china_map_chart.setOption(this.china_map_option); } }); }, }, mounted() { this.initChart(); this.initData(); }, } ``` 这里地图数据原始数据是自己引入的,官方文档提示,ECharts 中提供了两种格式的地图数据,一种是可以直接 script 标签引入的 js 文件,引入后会自动注册地图名字和数据。还有一种是 JSON 文件,需要通过 AJAX 异步加载后手动注册。 JavaScript 引入示例 ``` <script src="echarts.js"></script> <script src="map/js/china.js"></script> <script> var chart = echarts.init(document.getElementById('main')); chart.setOption({ series: [{ type: 'map', map: 'china' }] }); </script> ``` JSON 引入示例 ``` $.get('map/json/china.json', function (chinaJson) { echarts.registerMap('china', chinaJson); var chart = echarts.init(document.getElementById('main')); chart.setOption({ series: [{ type: 'map', map: 'china' }] }); }); ``` 我这里使用的是json数据,实际上我们不一定要使用ajax,只要获得数据即可,我利用下面的方式。 ``` export const china = {}; ``` 提供数据下载 : [中国地图](http://note.youdao.com/) , [世界地图](http://note.youdao.com/)

vue笔记1-模板语法

[TOC] # 1. 插值 ## 1.1. 文本 ``` //使用双大括号形式 <span>Message: {{ msg }}</span> //使用v-once会只执行一次,不会被更新 <span v-once>这个将不会改变: {{ msg }}</span> ``` ## 1.2. 原始HTML 双大括号会将数据解释为纯文本,如果想要输出纯html则需要使用v-html ``` //rawnhtml内容: <sapn style="color:red"> 样式生效则为红色 </span> //两种不同的使用方法 <p>Using mustaches: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p> ``` 使用双大括号 <sapn style="color:red"> 样式生效则为红色 </span> 使用v-html指令,文字显示为红色 样式生效则为红色 注意,使用v-html的时候要注意xss攻击,只对可信内容使用v-html ## 1.3. 特性 双大括号语法不能作用在 HTML 特性上,遇到这种情况应该使用 v-bind 指令: ``` //使用v-bind 在html的特性上 <div v-bind:id="dynamicId"></div> //下面这种写法,是不存在的 <div id="{{ dynamicId }}"></div> ``` ## 1.4. 使用js表达式 支持单个表达式 ``` {{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} //带有变量的字符串拼接 <div v-bind:id="'list-' + id"></div> ``` 下面列子不会生效 ``` <!-- 这是语句,不是表达式 --> {{ var a = 1 }} <!-- 流控制也不会生效,请使用三元表达式 --> {{ if (ok) { return message } }} ``` # 2. 指令 指令 (Directives) 是带有 v- 前缀的特殊属性 # 3. 缩写 ## 3.1 v-bind 缩写 ``` <!-- 完整语法 --> <a v-bind:href="url">...</a> <!-- 缩写 --> <a :href="url">...</a> ``` ## 3.2. v-on 缩写 ``` <!-- 完整语法 --> <a v-on:click="doSomething">...</a> <!-- 缩写 --> <a @click="doSomething">...</a> ```

vue笔记2-计算属性和观察者

# 1. 计算属性 模板内的表达式非常便利,但在模板中放入太多的逻辑会让模板过重且难以维护,所有就有了计算属性 例子: ``` //html代码 <div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> //js代码 var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } } }) ``` 结果: ``` Original message: "Hello" Computed reversed message: "olleH" ``` vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新 # 2. 计算属性 vs 方法 上面的列子通过在表达式中调用方法可以达到同样效果 ``` //html代码 <p>Reversed message: "{{ reversedMessage() }}"</p> //组件中 methods: { reversedMessage: function () { return this.message.split('').reverse().join('') } } ``` 不同点:计算属性是基于它们的**依赖进行缓存**的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。 如果依赖不存在,则下面的计算属性不会随时更新,每当重新触发渲染才会执行 ``` computed: { now: function () { //这里不存在依赖,则计算属性不会更新 return Date.now() } } ``` 缓存存在的意义是重大的,当你的计算很复杂时候,这将很有用,如果你不想要缓存,那么完全可以使用方法代替。 # 3. 计算属性(computed) vs 侦听属性(watch) 当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调 ``` <div id="demo">{{ fullName }}</div> ``` ``` var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } }) ``` 上面代码是命令式且重复的。将它与计算属性的版本进行比较: ``` var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } }) ``` 感受很明显,下面的代码好太多了 # 4. 计算属性的setter 计算属性默认只有 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] } } } ``` 以前当 firstname 和lastname变化时候,fullname会改变, 现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。 # 5. 侦听器不是一无是处 虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。当需要在数据变化时**执行异步**或**开销较大**的操作时,这个方式是最有用的。 ``` //html代码 <div id="watch-example"> <p> Ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div> //js代码 <!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 --> <!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 --> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script> <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 限制操作频率的函数。 // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率 // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于 // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识, // 请参考:https://lodash.com/docs#debounce 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> ``` 在这个示例中,使用 watch 选项允许我们执行**异步操作** (访问一个 API),限制我们执行该**操作的频率**,并在我们得到最终结果前,**设置中间状态**。这些都是**计算属性无法做到的**。

vue笔记5-class与style绑定

[TOC] 操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 v-bind 处理 最简单的想法是,只需要通过表达式计算出字符串结果即可。 但是在v-bind作用于class和style属性时,vue进行了专门的增强,字符串之外,还可以是对象或数组。 # 1 绑定HTML Class ## 1.1. 对象语法 ``` //模板: <div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"> </div> ``` ``` //data: data: { isActive: true, hasError: false } ``` 我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式: ``` <div v-bind:class="classObject"></div> ``` ``` data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } } ``` ## 1.2. 数组语法 ``` <div v-bind:class="[activeClass, errorClass]"></div> ``` ``` data: { activeClass: 'active', errorClass: 'text-danger' } ``` 也可以使用三元表达式 ``` <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> ``` 这样errorClass是用在的,activeClass只有当isactive为truthy时候才存在 不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法: ``` <div v-bind:class="[{ active: isActive }, errorClass]"></div> ``` # 2. 绑定内联样式 ## 2.1. 对象语法 ``` <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> data: { activeColor: 'red', fontSize: 30 } //直接绑定到一个样式对象通常更好,这会让模板更清晰: <div v-bind:style="styleObject"></div> data: { styleObject: { color: 'red', fontSize: '13px' } } <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> data: { activeColor: 'red', fontSize: 30 } ``` ## 2.2. 数组语法 v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上: ``` <div v-bind:style="[baseStyles, overridingStyles]"></div> ``` ## 2.3. 自动添加前缀 当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。 ## 2.4. 多重值 从 2.3.0 起你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如: <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div> 这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。

vue笔记3-vue条件渲染

# 1. v-if 实例 1. 基本使用 <h1 v-if="ok">Yes</h1> 2. 如果想切换多个元素,可以使用template元素,渲染结构不包含template元素 ``` <template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template> ``` 3. 使用else ``` <div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div> ``` 4. 使用v-else-if ``` <div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div> ``` # 2. 使用key管理可复用元素 不使用key,当切换登陆方式时候,input框中已经输入的内容不会变,只是placeholder中内容改变,这是因为两个模板使用了相同的元素,所以会复用,vue这样做是为了高效的渲染元素 ``` <template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address"> </template> ``` 使用key的情况下,每次切换时,input输入框都将被重新渲染,<label> 元素仍然会被高效地复用,因为它们没有添加 key 属性 ``` <template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username" key="username-input"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address" key="email-input"> </template> ``` # 3. v-show v-show切换元素的css属性 display <h1 v-show="ok">Hello!</h1> # 4. v-if vs v-show v-if 切换过程中条件块内的事件监听器和子组件适当地被销毁和重建,如果初始条件为假,则什么都不做,知道变为真时,才会开始渲染条件块 v-show 不管初始条件是什么,元素总会被渲染,只是简单基于css切换 根据条件灵活的选用 # 5. 注意 v-show 不支持 <template> 元素,也不支持 v-else 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。

vue笔记4-vue列表渲染

[TOC] # 1. v-for ## 1.1 遍历数组 ``` //item=>值,index=>索引 <ul id="example-2"> <li v-for="(item, index) in items"> {{ index }} - {{ item.message }} </li> </ul> ``` ``` var example2 = new Vue({ el: '#example-2', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] } }) ``` ``` //结果 0-Foo 1-Bar ``` ## 1.2 遍历对象 ``` //完整模式,value=>值,key=>键名,index=>索引 <div v-for="(value, key, index) in object"> {{ index }}. {{ key }}: {{ value }} </div> ``` ``` new Vue({ el: '#v-for-object', data: { object: { firstName: 'John', lastName: 'Doe', age: 30 } } }) ``` ``` //结果 0.firstName:John 1.lastName:Doe 2.age:30 ``` ## 1.3 key 当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略. 建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。 ``` <div v-for="item in items" :key="item.id"> <!-- 内容 --> </div> ``` # 2. 数组更新检测 ## 2.1 变异方法:使用这些方法会改变原始数组,并且会触发视图更新 - push() - pop() - shift() - unshift() - sort() - reverse() - splice() ``` //splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。 arrayObject.splice(index,howmany,item1,.....,itemX) //index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。 //howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。 //item1, ..., itemX 可选。向数组添加的新项目。 //例子 <script type="text/javascript"> var arr = new Array(6) arr[0] = "George" arr[1] = "John" arr[2] = "Thomas" arr[3] = "James" arr[4] = "Adrew" arr[5] = "Martin" //操作 //1. arr.splice(2,0,"William") //2. arr.splice(2,1,"William") //3. arr.splice(2,3,"William") //arr结果 //1. George,John,William,Thomas,James,Adrew,Martin //在index=2位置添加william //2. George,John,William,James,Adrew,Martin //删除index=2的原有元素,添加新元素 //3. George,John,William,Martin //从index=2位置删除掉3个元素,再添加新元素 </script> ``` ## 2.2 非变异方法:不会改变原始数组,会返回一个新的数组。例如 - filter() - concat() ``` //简单理解为合并数组 var arr = [`大漠`,'30','W3cplus']; console.log(arr); // ["大漠", "30", "W3cplus"] var arr2 = arr.concat('Blog','2014'); console.log(arr2); // ["大漠", "30", "W3cplus", "Blog", "2014"] //同时传递一个或多个数组 var arr = ["大漠","30"]; console.log(arr); // ["大漠", "30"] var arr2 = arr.concat(1,["blog","w3cplus"],["a","b","c"]); console.log(arr2); // ["大漠", "30", 1, "blog", "w3cplus", "a", "b", "c"] //还可以传递空值,只是复制数组并返回一个副本 var arr = [1,2]; console.log(arr); // [1, 2] var arr2 = arr.concat(); console.log(arr2); // [1, 2] ``` - slice() ``` //slice() 方法可从已有的数组中返回选定的元素。 arrayObject.slice(start,end) //start 必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。 //end 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。 ``` 使用非变异方法,使用生成的新数组替换原数组不会导致 Vue 丢弃现有 DOM 并重新渲染整个列表,因为vue使用了一些智能启发式的方法。 ## 2.3 注意事项 由于 JavaScript 的限制,Vue 不能检测以下变动的数组: - 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue 解决方法 ``` // 方法1 Vue.set Vue.set(example1.items, indexOfItem, newValue) // 方法2 Array.prototype.splice example1.items.splice(indexOfItem, 1, newValue) ``` - 当你修改数组的长度时,例如:vm.items.length = newLength 解决方法 ``` example1.items.splice(newLength) ``` ## 2.4 还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除: 还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除: ``` var vm = new Vue({ data: { a: 1 } }) // `vm.a` 现在是响应式的 vm.b = 2 // `vm.b` 不是响应式的 ``` 对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于: ``` var vm = new Vue({ data: { userProfile: { name: 'Anika' } } }) ``` 你可以添加一个新的 age 属性到嵌套的 userProfile 对象: Vue.set(vm.userProfile, 'age', 27) 你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名: vm.$set(this.userProfile, 'age', 27) 如果想复制多个属性 ``` this.userProfile = Object.assign({}, this.userProfile, { age: 27, favoriteColor: 'Vue Green' }) //Object.assign方法的第一个参数 (这里是'{}') 是目标对象,后面的参数都是源对象。 ``` ## 2.5 显示过滤/排序结果 有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。 例如: ``` <li v-for="n in evenNumbers">{{ n }}</li> data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(function (number) { return number % 2 === 0 }) } } ``` 在计算属性不适用的情况下 (例如,在嵌套 v-for 循环中) 你可以使用一个 method 方法: ``` <li v-for="n in even(numbers)">{{ n }}</li> data: { numbers: [ 1, 2, 3, 4, 5 ] }, methods: { even: function (numbers) { return numbers.filter(function (number) { return number % 2 === 0 }) } } ``` ## 2.6 v-for ``` <div> <span v-for="n in 10">{{ n }} </span> </div> //用在template上 <ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider"></li> </template> </ul> //用在组件上 <my-component v-for="item in items" :key="item.id"></my-component> ``` ## 2.7 v-for with v-if 当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用,如下: ``` <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li> ``` 上面的代码只传递了未 complete 的 todos。 而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 `<template>`)上。如: ``` <ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p> ```

vue笔记6-事件处理

# v-on监听dom事件 ``` //简单逻辑直接使用js代码,很方便 <div id="example-1"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div> var example1 = new Vue({ el: '#example-1', data: { counter: 0 } }) //复杂逻辑,调用一个方法 <div id="example-2"> <!-- `greet` 是在下面定义的方法名 --> <button v-on:click="greet">Greet</button> </div> var example2 = new Vue({ el: '#example-2', data: { name: 'Vue.js' }, // 在 `methods` 对象中定义方法 methods: { greet: function (event) { // `this` 在方法里指向当前 Vue 实例 alert('Hello ' + this.name + '!') // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName) } } } }) // 也可以用 JavaScript 直接调用方法 example2.greet() // => 'Hello Vue.js!' ``` # 修饰符

vue笔记7-表单输入绑定

# 文本 ``` <input v-model="message" placeholder="edit me"> <p>Message is: {{ message }}</p> ``` # 多行文本 ``` <span>Multiline message is:</span> <p style="white-space: pre-line;">{{ message }}</p> <br> <textarea v-model="message" placeholder="add multiple lines"></textarea> ``` # 复选框 ``` //单个复选框,绑定到布尔值: <input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label> //多个复选框,绑定到同一个数组: <div id='example-3'> <input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mike</label> <br> <span>Checked names: {{ checkedNames }}</span> </div> new Vue({ el: '#example-3', data: { checkedNames: [] } }) ``` # 单选按钮 ``` <div id="example-4"> <input type="radio" id="one" value="One" v-model="picked"> <label for="one">One</label> <br> <input type="radio" id="two" value="Two" v-model="picked"> <label for="two">Two</label> <br> <span>Picked: {{ picked }}</span> </div> new Vue({ el: '#example-4', data: { picked: '' } }) ``` # 选择框 1. 单选 ``` <div id="example-5"> <select v-model="selected"> <option disabled value="">请选择</option> <option>A</option> <option>B</option> <option>C</option> </select> <span>Selected: {{ selected }}</span> </div> new Vue({ el: '...', data: { selected: '' } }) ``` 如果 v-model 表达式的初始值未能匹配任何选项,`<select>` 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。 2. 多选时 (绑定到一个数组): ``` <div id="example-6"> <select v-model="selected" multiple style="width: 50px;"> <option>A</option> <option>B</option> <option>C</option> </select> <br> <span>Selected: {{ selected }}</span> </div> new Vue({ el: '#example-6', data: { selected: [] } }) ``` # 修饰符 - .lazy 在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步: ``` <!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg" > ``` - .number 如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符: <input v-model.number="age" type="number"> 这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。 - .trim 如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符: <input v-model.trim="msg"> # 值绑定 对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值): ``` <!-- 当选中时,`picked` 为字符串 "a" --> <input type="radio" v-model="picked" value="a"> <!-- `toggle` 为 true 或 false --> <input type="checkbox" v-model="toggle"> <!-- 当选中时,`selected` 为字符串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select> ``` 但是有时我们可能想把值绑定到 Vue 实例的一个**动态属性**上,这时可以用 v-bind 实现,并且这个属性的值**可以不是字符串**。 复选框 ``` <input type="checkbox" v-model="toggle" v-bind:true-value="a" v-bind:false-value="b" > // 当选中时 vm.toggle === vm.a // 当没有选中时 vm.toggle === vm.b ``` 单选按钮 ``` <input type="radio" v-model="pick" v-bind:value="a"> // 当选中时 vm.pick === vm.a ``` 选择框的选项 ``` <select v-model="selected"> <!-- 内联对象字面量 --> <option v-bind:value="{ number: 123 }">123</option> </select> // 当选中时 typeof vm.selected // => 'object' vm.selected.number // => 123 ```

vue笔记8-vue组件

[TOC] # 1. 全局注册 # 2. 局部注册 # 3. dom模板和字符串模板 - 当使用dom模板时候要注意有些元素内包含的元素会受到限制 - HTML 特性是不区分大小写的。所以,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 需要转换为相对应的 kebab-case - 当使用字符串模板时不会受到以上两点限制 # 4.父子组件的关系 prop 向下传递,事件向上传递。 父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。 例子 ``` //子组件内容 Vue.component('child', { // 声明 props props: ['message'], // 就像 data 一样,prop 也可以在模板中使用 // 同样也可以在 vm 实例中通过 this.message 来使用 template: '<span>{{ message }}</span>' }) //父组件调用子组件 <child message="hello!"></child> //结果 hello! ``` 以上使用字面量语法,当需要向子组件传递动态内容可以使用,使用动态prop ``` //父组件中 <div> <input v-model="parentMsg"> <br> <child v-bind:my-message="parentMsg"></child> </div> //v-bind 的缩写语法: <child :my-message="parentMsg"></child> ``` ``` //如果你想把一个对象的所有属性作为 prop 进行传递,可以使用不带任何参数的 v-bind (即用 v-bind 而不是 v-bind:prop-name)。例如,已知一个 todo 对象: todo: { text: 'Learn Vue', isComplete: false } //然后: <todo-item v-bind="todo"></todo-item> //将等价于: <todo-item v-bind:text="todo.text" v-bind:is-complete="todo.isComplete" ></todo-item> ``` # 5. 字面量语法vs动态语法 不能使用字面量语法传递数值 ``` <!-- 传递了一个字符串 "1" --> <comp some-prop="1"></comp> <!-- 传递真正的数值 --> <comp v-bind:some-prop="1"></comp> ``` # 6. 在子组件中修改父组件数据 prop是单向绑定的,父组件属性变化时候将传导给子组件,但是反过来不会,不能在子组件中改变prop 但是我们忍不住想修改它 正确的应对方式是: ``` //定义一个局部变量,并用 prop 的值初始化它: props: ['initialCounter'], data: function () { return { counter: this.initialCounter } } //定义一个计算属性,处理 prop 的值并返回: props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } } ``` 注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。 # 6. prop验证 # 7. 非prop特性 所谓非 prop 特性,就是指它可以直接传入组件,而不需要定义相应的 prop。 尽管为组件定义明确的 prop 是推荐的传参方式,组件的作者却并不总能预见到组件被使用的场景。所以,组件可以接收任意传入的特性,这些特性都会被添加到组件的根元素上。 ``` //在父组件上添加 data-3d-date-picker="true" <bs-date-input data-3d-date-picker="true"></bs-date-input> //属性 data-3d-date-picker="true" 会被自动添加到bs-date-input的根元素上。 ``` 替换或合并现有特性 当根元素和父组件都设置了同一个属性怎么处理? ``` //假设这是 bs-date-input 的模板: <input type="date" class="form-control"> //为了给该日期选择器插件增加一个特殊的主题,我们可能需要增加一个特殊的 class,比如: <bs-date-input data-3d-date-picker="true" class="date-picker-theme-dark" ></bs-date-input> ``` 在这个例子当中,我们定义了两个不同的 class 值: - form-control,来自组件自身的模板 - date-picker-theme-dark,来自父组件 对于多数特性来说,传递给组件的值会覆盖组件本身设定的值。即例如传递 type="large" 将会覆盖 type="date" 且有可能破坏该组件!所幸我们对待 class 和 style 特性会更聪明一些,这两个特性的值都会做合并 (merge) 操作,让最终生成的值为:form-control date-picker-theme-dark。 # 8. 子组件向父组件通信,自定义事件 当子组件想和父组件通信时候,可以使用自定义事件 ``` //父组件 <div id="counter-event-example"> <p>{{ total }}</p> <!-- v-on:increment 给子组件一个可以调用increment事件 --> <button-counter v-on:increment="incrementTotal"></button-counter> <button-counter v-on:increment="incrementTotal"></button-counter> </div> ``` ``` Vue.component('button-counter', { template: '<button v-on:click="incrementCounter">{{ counter }}</button>', data: function () { return { counter: 0 } }, methods: { incrementCounter: function () { this.counter += 1 //点击子组件中的时间后,调用父组件中绑定的事件 this.$emit('increment') } }, }) new Vue({ el: '#counter-event-example', data: { total: 0 }, methods: { //子组件和父组件通信后,调用自身的方法,来改变父组件中的属性 incrementTotal: function () { this.total += 1 } } }) ``` # 9. 双向通信,.sync修饰符 ``` //如下代码 <comp :foo.sync="bar"></comp> //会被扩展为: <comp :foo="bar" @update:foo="val => bar = val"></comp> //当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件: this.$emit('update:foo', newValue) ``` # 10. 非父子组件间通信 可以使用状态管理模式 # 11. 使用插槽分发内容 作用域,父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。 一个常见错误是试图在父组件模板内将一个指令绑定到子组件的属性/方法: ``` <!-- 无效 --> <child-component v-show="someChildProperty"></child-component> ``` - 单个插槽 ``` //假定 my-component 组件有如下模板: <div> <h2>我是子组件的标题</h2> <slot> 只有在没有要分发的内容时才会显示。 </slot> </div> //父组件模板: <div> <h1>我是父组件的标题</h1> <my-component> <p>这是一些初始内容</p> <p>这是更多的初始内容</p> </my-component> </div> //渲染结果: <div> <h1>我是父组件的标题</h1> <div> <h2>我是子组件的标题</h2> <p>这是一些初始内容</p> <p>这是更多的初始内容</p> </div> </div> ``` - 具名插槽 ``` //例如,假定我们有一个 app-layout 组件,它的模板为: <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> //父组件模板: <app-layout> <h1 slot="header">这里可能是一个页面标题</h1> <p>主要内容的一个段落。</p> <p>另一个主要段落。</p> <p slot="footer">这里有一些联系信息</p> </app-layout> //渲染结果为: <div class="container"> <header> <h1>这里可能是一个页面标题</h1> </header> <main> <p>主要内容的一个段落。</p> <p>另一个主要段落。</p> </main> <footer> <p>这里有一些联系信息</p> </footer> </div> ``` - 作用域插槽 实质上是通过子组件中的数据传递到父组件,更改父组件中已经渲染好的视图 ``` //在子组件中,将数据传递到插槽 <div class="child"> <slot text="hello from child"></slot> </div> //在父级中,具有特殊特性 slot-scope 的 <template> 元素必须存在,表示它是作用域插槽的模板。slot-scope 的值将被用作一个临时变量名,此变量接收从子组件传递过来的 prop 对象: <div class="parent"> <child> <template slot-scope="props"> <span>hello from parent</span> <span>{{ props.text }}</span> </template> </child> </div> //如果我们渲染上述模板,得到的输出会是: <div class="parent"> <div class="child"> <span>hello from parent</span> <span>hello from child</span> </div> </div> ``` 应用 ``` //作用域插槽更典型的用例是在列表组件中,允许使用者自定义如何渲染列表的每一项: <my-awesome-list :items="items"> <!-- 作用域插槽也可以是具名的 --> <li slot="item" slot-scope="props" class="my-fancy-item"> {{ props.text }} </li> </my-awesome-list> //列表组件的模板: <ul> <slot name="item" v-for="item in items" :text="item.text"> <!-- 这里写入备用内容 --> </slot> </ul> ``` # 12. 动态组件和keep-alive ``` var vm = new Vue({ el: '#example', data: { currentView: 'home' }, components: { home: { /* ... */ }, posts: { /* ... */ }, archive: { /* ... */ } } }) <component v-bind:is="currentView"> <!-- 组件在 vm.currentview 变化时改变! --> </component> ``` ``` //如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数: <keep-alive> <component :is="currentView"> <!-- 非活动组件将被缓存! --> </component> </keep-alive> ``` # 13. 内联模板 如果子组件有 inline-template 特性,组件将把它的内容当作它的模板,而不是把它当作分发内容。这让模板编写起来更灵活。 ``` <my-component inline-template> <div> <p>这些将作为组件自身的模板。</p> <p>而非父组件透传进来的内容。</p> </div> </my-component> ``` 但是 inline-template 让模板的作用域难以理解。使用 template 选项在组件内定义模板或者在 .vue 文件中使用 template 元素才是**最佳实践**。 element ui中table自定义内容 ``` <el-table :data="props.row.strategies" style="width:100%;"> //使用inline-template <el-table-column inline-template label="可访问用户组" width="180"> <div> <el-tag v-for="user in row.user_group" :key="user.id">{{ user.name }}</el-tag> </div> </el-table-column> //使用slot-scop,官方推荐 <el-table-column label="操作"> <template slot-scope="strategyScope"> <div> <el-tooltip content="编辑" placement="top" effect="light"> <el-button size="common" class="edit" type="text" @click="editStrategy(strategyScope.row)"><i class="el-icon-ui-edit"></i></el-button> </el-tooltip> <el-tooltip content="删除" placement="top" effect="light"> <el-button size="common" class="apply" type="text" @click="deleteStrategy(strategyScope.row)"> <i class="el-icon-delete2"></i></el-button> </el-tooltip> </div> </template> </el-table-column> </el-table> ``` # 14. X-Template 另一种定义模板的方式是在 JavaScript 标签里使用 text/x-template 类型,并且指定一个 id。例如: ``` <script type="text/x-template" id="hello-world-template"> <p>Hello hello hello</p> </script> Vue.component('hello-world', { template: '#hello-world-template' }) ``` 这在有很多大模板的演示应用或者特别小的应用中可能有用,其它场合应该避免使用,因为这将模板和组件的其它定义分离了。 # 15. 对低开销的静态组件使用 v-once 尽管在 Vue 中渲染 HTML 很快,不过当组件中包含**大量静态内容**时,可以考虑使用 v-once 将渲染结果缓存起来,就像这样: ``` Vue.component('terms-of-service', { template: '\ <div v-once>\ <h1>Terms of Service</h1>\ ...很多静态内容...\ </div>\ ' }) ```