Vue官网
第一个Vue程序 在教程安装里给出了多种引入方式,具体点击这里
直接</script>
引入 在编辑器上输入html回车,这时候就会自动补全以下代码
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html> <html lang="en" dir="ltr" > <head> <meta charset="utf-8" > <title></title> </head> <body> </body> </html>
引入 Vue
1 2 <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" ></script>
在页面输出hello Vue,用浏览器打开文件并检验效果<div>hello Vue</div>
可以之后我们改造源文件让它与Vue产生关联:new 一个Vue实例
1 2 3 4 5 6 7 8 <script type ="text/javascript" > new Vue({ el: '#app' , //挂载到指定节点上 data: { message: 'Hello Vue!' } }) </script>
所挂载的节点需要添加一个id<div id="app"></div>
最终源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <meta http-equiv="X-UA-Compatible" content="ie=edge" > <title>Document</title> </head> <body> <div id="app" > {{message}} {{message + message}} <div :id="message" ></div> <ul> <li v-for="item in list" > <span v-if="!item.del" >{{item.title}}</span> <span v-else style="text-decoration: line-through" >{{item.title}}</span> <button v-show="!item.del" >删除</button> </li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" ></script> <script> var vm = new Vue({ el: '#app' , data: { message: 'hello world' , list: [{ title: '课程1' , del: false }, { title: '课程2' , del: true }], } }) </script> </body> </html>
此时用浏览器打开即可见到显示Hello Vue!
参考官方demo 链接
添加方法 添加显示
1 2 3 4 <div class="addMethod" > <input type ="text" name="" value="" v-model="info" > <button type ="button" name="button" @click="handleClick" >add</button> </div>
其中
v-model是Vue里面的双向绑定,
v-for是循环遍历
@click=”handleClick” 绑定方法
console.log(this.info) 打印信息,如何查看打印输出:浏览器-右键-检查-Console
handleClick方法生命在methods对象里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html> <html lang="en" dir="ltr" > <head> <meta charset="utf-8" > <title></title> </head> <body> </body> <div id="app" > {{ message }} <div class="addMethod" > <input type ="text" name="" value="" v-model="info" > <button type ="button" name="button" @click="handleClick" >add</button> </div> <ul> <li v-for="item in list" >{{item}}</li> </ul> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" ></script> <script type ="text/javascript" > new Vue({ el: '#app' , //挂载到指定节点上 data: { message: 'Hello Vue!' , info: '' , list:[], }, methods: { handleClick (){ //console this.list.push(this.info) this.info = '' } } }) </script> </html>
使用自定义组件 定义名为 todo-item 的新组件
1 2 3 4 Vue.component('todo-item' ,{ props:['item' ], template: '<li class="item">{{item}}</li>' })
创建一个 todo-item 组件的实例,并传递属性值 v-bind:item="item"
或者简写成:item="item"
<todo-item v-for="item in list" :item="item"></todo-item>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <!DOCTYPE html> <html lang="en" dir="ltr" > <head> <meta charset="utf-8" > <title></title> <style media="screen" > .item { color:red; } </style> </head> <body> </body> <div id="app" > {{ message }} <div class="addMethod" > <input type ="text" name="" value="" v-model="info" > <button type ="button" name="button" @click="handleClick" >add</button> </div> <ul> <todo-item v-for="item in list" :item="item" >{{item}}</todo-item> </ul> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" ></script> <script type ="text/javascript" > Vue.component('todo-item' ,{ props:['item' ], template: '<li class="item">{{item}}</li>' }) new Vue({ el: '#app' , //挂载到指定节点上 data: { message: 'Hello Vue!' , info: '' , list:[], }, methods: { handleClick (){ console.log(this.info) this.list.push(this.info)//往list数组添加数据 this.info = '' //每次点击add的同时,清空输入框 } } }) </script> </html>
以上操作有几个缺点
全局定义:强制要求每个component
中的命名不得重复
字符串模版:缺乏语法高亮,在html有多行的时候,需要用\
不支持CSS:意味着当html和JavaScript组件化时,CSS明显被遗漏
没有构建步骤:限制只能用html和ES5 JavaScript,而不能使用预处理器,如Pug(formerly Jade)和Babel,即每次都需要手动在浏览器上刷新,没有自动热更新。
npm安装 安装Vuebash npm install vue
安装命令行工具 (CLI)Vue CLI
npm install -g @vue/cli
出现安装问题
1 2 3 4 5 6 7 8 npm WARN deprecated joi@14.3.1: This module has moved and is now available at @hapi/joi. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues. npm WARN deprecated topo@3.0.3: This module has moved and is now available at @hapi/topo. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues. npm WARN deprecated hoek@6.1.3: This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues. npm WARN deprecated cross-spawn-async@2.2.5: cross-spawn no longer requires a build toolchain, use it instead npm ERR! Unexpected end of JSON input while parsing near '...TGOVzYcDOP1jLScCp0ACN' npm ERR! A complete log of this run can be found in : npm ERR! /Users/sam/.npm/_logs/2019-04-29T01_23_19_163Z-debug.log
清楚一下缓存npm cache clean --force
运行vue --version
出现版本信息则说明安装成功
创建新项目vue create my-app
使用默认安装
1 2 3 4 Vue CLI v3.7.0 ? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) Manually select features
启动项目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 > my-app@0.1.0 serve /Users/sam/Documents/studyUp/my-app > vue-cli-service serve INFO Starting development server... 98% after emitting CopyPlugin . DONE Compiled successfully in 6412ms 上午9:53:14 App running at: - Local: http://localhost:8080/ - Network: http://192.168.43.116:8080/ Note that the development build is not optimized. To create a production build, run npm run build. ``` 打开localhost:8080即可见到效果。 ![image.png](https://upload-images.jianshu.io/upload_images/3850802-8fcdd44c56b23d35.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 其中 1 node_modules 整个项目的依赖 2 pubic ico 图标 3 pubic index.html 整个项目的载体<div id="app" ></div> 跟上面👆直接`</script>`引入的div一样 4 src 整个项目的源代码 5 Main.js 项目入口文件 6 babel.config.js babel配置 7 package.json 依赖包版本 8 说明 将上面的直接</script>引入的demo改成单文件形式👇三个模块独立 - 模版 template - 逻辑 script - 样式 style <style scoped>样式作用域只在该文件内有效 在App.vue文件的模版中替换`div`内容,新建主件TodoItem.vue,在App.vue引入并使用 ```bash <template> <li class="item" >{{item}}</li> </template> <script> export default { props: ['item' ], } </script> <style scoped> .item { color: red; } </style>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <template> <div id="app" > {{ message }} <div class="addMethod" > <input type ="text" name="" value="" v-model="info" > <button type ="button" name="button" @click="handleClick" >add</button> </div> <ul> <todo-item v-for="item in list" :key="item" :item="item" >{{item}}</todo-item> </ul> </div> </template> <script> import TodoItem from './components/TodoItem.vue' //引入TodoItem export default { name: 'app' , data () { return { message: 'hello vue' , info: '' , list: [], } }, methods: { handleClick () { this.list.push(this.info) this.info = '' } }, components: { TodoItem, //注册TodoItem } } </script> <style> </style>
1 <todo-item v-for="item in list" :key="item" :item="item" >{{item}}</todo-item>
上面👆是通过属性来传递item,下面👇改成插槽的方式
1 <span style="font-size:20px" >{{item}}</span>
此时解析由<li class="item"></li>
改成
1 2 3 <li class="item" > <slot></slot> </li>
或者给插槽一个名字(具名插槽)
1 2 3 4 5 <todo-item v-for="item in list" :key="item" > <template id="" v-slot:item > <span style="font-size:20px" >{{item}}</span> </template> </todo-item>
1 2 3 4 5 <template> <li class="item" > <slot name="item" ></slot> </li> </template>
作用域插槽(使用场景:由子控件传递值给父控件,供父控件使用修改显示,选中由红变蓝)
1 2 3 4 5 <todo-item v-for="item in list" :key="item" > <template id="" v-slot:item="itemProps" > 获取checked的值 <span :style="{fontSize:'20px', color: itemProps.checked ? 'red' : 'blue' }" >{{item}}</span> </template> </todo-item>
1 2 3 4 5 6 <template> <li class="item" > <input type ="checkbox" v-model="checked" > <slot name="item" v-bind="{{checked}}" ></slot> //将checked 传递出去 </li> </template>
动手复制代码跑来看看效果吧 👇👇👇👇👇👇👇👇👇
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template> <li class="item" > <input type ="checkbox" v-model="checked" > <slot name="item" v-bind="{checked}" ></slot> </li> </template> <script> export default { props: ['item' ], data () { return { checked:false } } } </script> <style scoped> .item { color: red; } </style>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <template> <div id="app" > {{ message }} <div class="addMethod" > <input type ="text" name="" value="" v-model="info" > <button type ="button" name="button" @click="handleClick" >add</button> </div> <ul> <todo-item v-for="item in list" :key="item" > <template id="" v-slot:item="itemProps" > <span :style="{fontSize:'20px', color: itemProps.checked ? 'red' : 'blue' }" >{{item}}</span> </template> </todo-item> </ul> </div> </template> <script> import TodoItem from './components/TodoItem.vue' //引入TodoItem export default { name: 'app' , data () { return { message: 'hello vue' , info: '' , list: [], } }, methods: { handleClick () { this.list.push(this.info) this.info = '' } }, components: { TodoItem, //注册TodoItem } } </script> <style> </style>
条件渲染 条件渲染官方文档地址 v-if
v-else
v-show
v-for
组件基础&组件注册 组件基础
组件注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <meta http-equiv="X-UA-Compatible" content="ie=edge" > <title>Document</title> </head> <body> <div id="app" > {{message}} {{message + message}} <div :id="message" ></div> <!-- <ul> <todo-item v-for="item in list" :title="item.title" :del="item.del" ></todo-item> </ul> --> <todo-list></todo-list> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" ></script> <script> Vue.component('todo-item' , { props: { title: String, del: { type : Boolean, default: false , }, }, template: ` <li> <span v-if="!del" >{{title}}</span> <span v-else style="text-decoration: line-through" >{{title}}</span> <button v-show="!del" >删除</button> </li> `, data: function () { return {} }, methods: { }, }) Vue.component('todo-list' , { template: ` <ul> <todo-item v-for="item in list" :title="item.title" :del="item.del" ></todo-item> </ul> `, data: function () { return { list: [{ title: '课程1' , del: false }, { title: '课程2' , del: true }], } } }) var vm = new Vue({ el: '#app' , data: { message: 'hello world' , } }) </script> </body> </html>
在Vue中,组件:小型的,一个个独立,可以复用的UI模块。它有三大概念:属性、事件、插槽
属性 Prop
自定义属性props
原生属性attrs
没有生命的属性,默认自动挂载到组件根元素上
设置inheritAttrs 为false可以关闭自动挂载
特殊属性 class、style
挂载到组件根元素上
支持字符串、对象、数组等多种语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <template> <div> name: {{ name }} <br /> type : {{ type }} <br /> list: {{ list }} <br /> isVisible: {{ isVisible }} <br /> <button @click="handleClick" >change type </button> </div> </template> <script> export default { name: "PropsDemo" , // inheritAttrs: false , // props: ['name' , 'type' , 'list' , 'isVisible' ], props: { name: String, type : { validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ["success" , "warning" , "danger" ].includes(value); } }, list: { type : Array, // 对象或数组默认值必须从一个工厂函数获取 default: () => [] }, isVisible: { type : Boolean, default: false }, onChange: { type : Function, default: () => {} } }, methods: { handleClick () { // 不要这么做、不要这么做、不要这么做 // this.type = "warning" ; // 可以,还可以更好 this.onChange(this.type === "success" ? "warning" : "success" ); } } }; </script>
事件 事件处理
普通时间
@click,@input,@change,@xxx等事件
通过this.$emit(‘xxx’,…)触发
修饰符事件
@input.trim,@click.stop,@submit.prevent 等
一般用于原生html元素,自定义组件需要自行开发支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 <template> <div> name: {{ name || "--" }} <br /> <input :value="name" @change="handleChange" /> <br /> <br /> <div @click="handleDivClick" > <button @click="handleClick" >重置成功</button> <button @click.stop="handleClick" >重置失败</button> </div> </div> </template> <script> export default { name: "EventDemo" , props: { name: String }, methods: { handleChange(e) { this.$emit ("change" , e.target.value); }, handleDivClick () { this.$emit ("change" , "" ); }, handleClick(e) { // 都会失败 //e.stopPropagation(); } } }; </script> ``` ```bash <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <meta http-equiv="X-UA-Compatible" content="ie=edge" > <title>Document</title> </head> <body> <div id="app" > {{message}} {{message + message}} <div :id="message" ></div> <!-- <ul> <todo-item v-for="item in list" :title="item.title" :del="item.del" ></todo-item> </ul> --> <todo-list></todo-list> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" ></script> <script> Vue.component('todo-item' , { props: { title: String, del: { type : Boolean, default: false , }, }, template: ` <li> <span v-if="!del" >{{title}}</span> <span v-else style="text-decoration: line-through" >{{title}}</span> <button v-show="!del" @click="handleClick" >删除</button> </li> `, data: function () { return {} }, methods: { handleClick(e) { console.log('点击删除按钮' ) this.$emit ('delete' , this.title) } }, }) Vue.component('todo-list' , { template: ` <ul> <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del" ></todo-item> </ul> `, data: function () { return { list: [{ title: '课程1' , del: false }, { title: '课程2' , del: true }], } }, methods: { handleDelete(val) { console.log('handleDelete' , val) } } }) var vm = new Vue({ el: '#app' , data: { message: 'hello world' , } }) </script> </body> </html>
插槽 插槽
普通插槽
<template slot="xxx">...</template>
<template v-slot:xxx>...</template>
作用域插槽
<template slot="xxx" slot-scope="props">...</template>
<template v-slot:xxx="props">...</template>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 <template> <div> <slot /> <slot name="title" /> <slot name="item" v-bind="{ value: 'vue' }" /> </div> </template> <script> export default { name: "SlotDemo" }; </script> ``` 我们可以理解为万物皆“属性”,即它们都是父组件传递给子组件,然后子组件根据传递的内容来执行对应的行为。请看demo ```bash <template> <div> {{ name }} <br /> <button @click="handleChange" >change name</button> <br /> <!-- {{ slotDefault }} --> <VNodes :vnodes="slotDefault" /> <br /> <VNodes :vnodes="slotTitle" /> <br /> <VNodes :vnodes="slotScopeItem({ value: 'vue' })" /> </div> </template> <script> export default { name: "BigProps" , components: { VNodes: { functional: true , render: (h, ctx) => ctx.props.vnodes } }, props: { name: String, onChange: { type : Function, default: () => {} }, slotDefault: Array, slotTitle: Array, slotScopeItem: { type : Function, default: () => {} } }, methods: { handleChange () { this.onChange("Hello vue!" ); } } }; </script> ``` ```bash <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <meta http-equiv="X-UA-Compatible" content="ie=edge" > <title>Document</title> </head> <body> <div id="app" > {{message}} {{message + message}} <div :id="message" ></div> <!-- <ul> <todo-item v-for="item in list" :title="item.title" :del="item.del" ></todo-item> </ul> --> <todo-list> <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del" > <template v-slot:pre-icon="{value}" > <span>前置图标 {{value}}</span> </template> </todo-item> </todo-list> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" ></script> <script> Vue.component('todo-item' , { props: { title: String, del: { type : Boolean, default: false , }, }, template: ` <li> <slot name="pre-icon" :value="value" ></slot> <span v-if="!del" >{{title}}</span> <span v-else style="text-decoration: line-through" >{{title}}</span> <slot name="suf-icon" >😄</slot> <button v-show="!del" @click="handleClick" >删除</button> </li> `, data: function () { return { value: Math.random() } }, methods: { handleClick(e) { console.log('点击删除按钮' ) this.$emit ('delete' , this.title) } }, }) Vue.component('todo-list' , { template: ` <ul> <slot></slot> </ul> `, data: function () { return { } }, }) var vm = new Vue({ el: '#app' , data: { message: 'hello world' , list: [{ title: '课程1' , del: false }, { title: '课程2' , del: true }], }, methods: { handleDelete(val) { console.log('handleDelete' , val) } } }) </script> </body> </html>
理解单文件组件 意思是通过Vue CLI 构建整个项目,而不是单个html文件。
双向绑定和单向数据流不冲突 v-model创建双向绑定 双向绑定:数据更新时,视图同步更新;反过来视图更新时,数据也会更新。可以通过v-model
来创建双向绑定。
v-model
文档在表单输入绑定 有说明:
v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
text 和 textarea 元素使用 value 属性和 input 事件;
checkbox 和 radio 使用 checked 属性和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件。
自定义组件的 v-model
.sync修饰符 创建双向绑定 .sync 修饰符
单向数据流 单向数据流
总结
Vue是单向数据流,不是双向绑定
Vue的双向绑定不过是语法糖
Object.defineProperty是用来做响应式更新的,和双向绑定没关系
v-model来实现双向绑定的实质是:属性的传递和事件的回调来做一个数据更新。所以说它仅仅是语法糖而已。
如何触发组件的更新 数据来源(单向的)
来自父类元素的属性
来自组件自身的状态 data
来自状态管理器
状态data 和 属性props 区别
状态是组件自身的数据
属性来自父组件的数据
状态或者属性的改变未必会让视图更新(只有做了响应才会更新,即放在了return里面)
合理应用计算属性和侦听器 计算属性computed
减少模版中的计算逻辑
数据缓存
以来固定的数据类型(响应式编程)
侦听器watch中可以执行任何逻辑,如函数节流,Ajax异步获取数据,甚至操作com
总之,computed能做的,watch都能做,反之则不行;能用computed的尽量用computed;
生命周期的应用场景和函数式组件 生命周期图示
函数式组件
functional:true 可以将它看作一个方法
无状态、无实力、没有this上下文、无生命周期
指令的本质是什么 源码演示
provide & inject provide / inject
源码演示
解决跨组件间的属性传递问题。
获取跨层级组件实例 源码演示
template VS JSX 源码演示
Vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
state,驱动应用的数据源;
view,以声明方式将 state 映射到视图;
actions,响应在 view 上的用户输入导致的状态变化。
在Vue中使用Vuex
源码演示
Vuex原理
State
提供一个响应式数据
Getter
借助Vue的计算属性computed来实现缓存
Mutation
更改state方法
Action
触发mutation方法
Module
Vue.set动态添加state到响应式数据中
Vue Router Vue Router
源码演示
Nuxt Nuxt
开发插件 vs code插件:Vetur:代码块功能
ESLint:代码规范、错误检查、
Prettier:代码规范提示
单元测试 三种方式:
jest或者mocha
@vue/test-utils
sinon
1 2 3 4 5 6 vue create XX Manually select features 除了默认之外还需要勾选 Unit Testing 选用ESLint + Standard config Lint on save JEST
demo
实战 创建工程 1 ➜ Vue_app git:(master) vue create vue-app
手动选择
1 2 3 4 5 6 7 8 9 ◉ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◉ Router ◉ Vuex ◉ CSS Pre-processors ◉ Linter / Formatter ❯◉ Unit Testing ◯ E2E Testing
其他选项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ? Use history mode for router? (Requires proper server setup for index fallback in produc tion) Yes ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by defaul t): Less ? Pick a linter / formatter config: Prettier ? Pick additional lint features: ◉ Lint on save ❯◉ Lint and fix on commit ? Pick additional lint features: Lint on save, Lint and fix on commit ? Pick a unit testing solution: Jest ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? Yes ? Save preset as: vue-app ? Pick the package manager to use when installing dependencies: Yarn
启动
1 2 $ cd vue-app $ yarn serve
安装两个依赖包(上面步骤创建工程时选择了Yarn构建,现在通过npm安装依赖包也是可以的原来)
1 npm i ant-design-vue moment
更改构建方式 文档说明
1 2 3 vi ~/.vuerc 修改: "packageManager" : "yarn" , source ~/.vuerc
我自己也重新操作了一下,项目构建成功之后的提示为
1 2 $ cd vue-app $ npm run serve
webpack配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ERROR Failed to compile with 1 errors 7:20:06 PM error in ./node_modules/ant-design-vue/dist/antd.less Module build failed (from ./node_modules/less-loader/dist/cjs.js): // https://github.com/ant-design/ant-motion/issues/44 .bezierEasingMixin(); ^ Inline JavaScript is not enabled. Is it set in your options? in /Users/samtake/Documents/GitHub/Vue_app/vue-app/node_modules/ant-design-vue/lib/style/color/bezierEa sing.less (line 110, column 0) @ ./node_modules/ant-design-vue/dist/antd.less 4:14-188 14:3-18:5 15:22-196 @ ./src/main.js @ multi (webpack)-dev-server/client?http://192.168.0.103:8086/sockjs-node (webpack)/hot/dev-server.js ./src/ main.js
css.loaderOptions
新建一个vue.config.js
文件 1 2 3 4 5 6 7 8 9 10 module.exports = { css: { loaderOptions: { less: { // 这里的选项会传递给 css-loader javascriptEnabled:true } } } }
重新启动构建npm run serve
按需加载 文档
配置babel.config.js文件
1 2 3 4 5 6 7 8 9 "plugins" : [ ["import" , { "libraryName" : "ant-design-vue" , "libraryDirectory" : "es" , "style" : true }] // `style: true ` 会加载 less 文件 ] import {Button} from 'ant-design-vue' ; npm i --save-dev babel-plugin-import
路由 同时刷新URL路径的插件
有两种加载方式,推荐这种:
1 component: { render: h => h("router-view" ) },
App.vue
1 2 3 4 5 6 7 8 9 10 11 <template> <div id="app" > <div id="nav" > <router-link to="/dashboard/analysis" >dashboard</router-link> | <router-link to="/form" >form</router-link> </div> <router-view /> </div> </template> <style lang="less" ></style>
BasicLayout.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template> <div> <Header /> <SiderMenu /> <router-view></router-view> <Footer /> </div> </template> <script> import Header from "./Header" ; import Footer from "./Footer" ; import SiderMenu from "./SiderMenu" ; export default { components: { Header, Footer, SiderMenu } }; </script> <style></style>
布局 Layout 布局
菜单和路由的结合 layouts
文件夹是菜单的布局
router.js
有name的才会渲染到菜单
不渲染到菜单,可以添加标志位hideInMenu: true,
如果不想让菜单的子路由渲染出来,可以添加标志位:hideChildrenMenu: true,
如何使用echarts第三方库
npm i XX
在component文件夹中进行封装
第三方库resize-detector
的作用是监听空间大小变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <template> <div ref="chartDom" ></div> </template> <script> import echarts from "echarts" ; import debounce from "lodash/debounce" ; import { addListener, removeListener } from "resize-detector" ; export default { props: { option: { type : Object, default: () => {} } }, watch: { option(val) { this.chart.setOption(val); } // option: { // handler(val) { // this.chart.setOption(val); // }, // deep: true // } }, created () { this.resize = debounce(this.resize, 300); }, mounted () { this.renderChart(); addListener(this.$refs .chartDom, this.resize); }, beforeDestroy () { removeListener(this.$refs .chartDom, this.resize); this.chart.dispose(); this.chart = null; }, methods: { resize () { console.log("resize" ); this.chart.resize(); }, renderChart () { // 基于准备好的dom,初始化echarts实例 this.chart = echarts.init(this.$refs .chartDom); this.chart.setOption(this.option); } } }; </script> <style></style>
mock的使用 1.在mock文件夹创建对应的js文件 2.在vue.config.js配置对应的代理 3.axios
像正常那样请求数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 devServer: { proxy: { "/api" : { target: "http://localhost:3000" , bypass: function (req, res) { if (req.headers.accept.indexOf("html" ) !== -1) { console.log("Skipping proxy for browser request." ); return "/index.html" ; } else if (process.env.MOCK !== "none" ) { const name = req.path .split("/api/" )[1] .split("/" ) .join("_" ); const mock = require(`./mock/${name} `); const result = mock(req.method); delete require.cache[require.resolve(`./mock/${name} `)]; return res.send(result); } } } }
axios的使用
切换环境的时候需要执行npm run serve:no-mock
才会请求真实服务器
一般都会对axios进行封装
表单的使用 表单的自动校验 分步表单(Vuex、Vue-router)
自己封装一个表单 components-ReceiverAccount.vue