前言:整合了以前的笔记,加入新记的笔记信息
javascript
<!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>test</title> </head> <body> <div id="app"> <ul> <li>{{message}}</li> </ul> <input type="text" v-model="message" />{{message}} </div> <script src="../../js/vue.js"></script> <script> const app = new Vue({ el: "#app", data: { message: "hello world" } }); </script> </body> </html>
v-model.lazycss
只有在input失去焦点的时候才会刷新数据html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../../js/vue.js"></script> </head> <body> <div id="app"> <ul> <li>{{message}}</li> </ul> <!-- 失去焦点或者enter才会更新值--> <input type="text" v-model.lazy="message">{{message}} </div> <script> const app = new Vue({ el: "#app", data: { message: "hello world" } }); </script> </body> </html>
order | 语法糖 | 做用 |
---|---|---|
v-bind=attr | :attr | 将数据和标签帮顶起来 |
v-on:action=actionName | @action=actionName | 绑定事件,事件不须要传参数能够省略() |
{{expression}} | 从vue对象中取值 | |
v-if="boolean" | if | |
v-else-if="boolean" | else if | |
v-else | else | |
key | 做为一种标签的身份标识 | |
v-show="boolean" | 和v-if的区别是,它是display=none标签还在 | |
v-for="(item, index) in items" | for循环 | |
:class="{className:boolean}" | 也能够用数组 | |
v-model="entity" | 双向绑定表单,其实是两个指令结合v-bind:value和v-on:input | |
v-html | 输出真正的 HTML | |
v-once | 写在标签上只渲染一次 | |
$forceUpdate |
强制更新数据 | |
export default { name: "Home", created() { console.log("Home组件被建立成功"); } };
未挂载成功的元素不能被获取到前端
export default { name: "Home", created() { console.log("Home组件被建立成功"); }, mounted(){ console.log("组件被挂载成功") } };
updated(): 当组件中发生变化时vue
export default { name: "Home", created() { console.log("Home组件被建立成功"); }, mounted(){ console.log("组件被挂载成功") }, updated(){ console.log("组件中发生改变时"); } };
其余java
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="vue.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div id="app"> {{msg}} </div> <script type="text/javascript"> var vm = new Vue({ el : "#app", data : { msg : "hi vue", }, //在实例初始化以后,数据观测 (data observer) 和 event/watcher 事件配置以前被调用。 beforeCreate:function(){ console.log('beforeCreate'); }, /* 在实例建立完成后被当即调用。 在这一步,实例已完成如下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。 然而,挂载阶段还没开始,$el 属性目前不可见。 */ created :function(){ console.log('created'); }, //在挂载开始以前被调用:相关的渲染函数首次被调用 beforeMount : function(){ console.log('beforeMount'); }, //el 被新建立的 vm.$el 替换, 挂在成功 mounted : function(){ console.log('mounted'); }, //数据更新时调用 beforeUpdate : function(){ console.log('beforeUpdate'); }, //组件 DOM 已经更新, 组件更新完毕 updated : function(){ console.log('updated'); } }); setTimeout(function(){ vm.msg = "change ......"; }, 3000); </script> </body> </html>
须要给标签添加一个key的属性就能够了,是由于虚拟dom的问题node
<!-- 当items是数组 --> <ul v-for="(item, index) in items" :key="item"> <li></li> </ul> <!-- 当items是对象,默认是取value --> <ul v-for="value in obj" :key="value"> <li></li> </ul> <!-- 当items是对象,默认是取value,key,index --> <ul v-for="(value,key,index) in obj" > <li></li> </ul>
支持响应式的方法python
pop()删除最后一个元素jquery
push(a,b...)追加一【多】个元素linux
shift()删除第一个元素
unshift(a,b...)添加【多】元素到第一个的位置
sort([compare])
reverse()
splice(起始位置,删除几个【只填第一个参数就是从第一个位置删除到完】,从第一个参数位置添加【能够多个】的元素)
Vue.set(src,index,newValue) 修改src 中index位置值,或者对象赋值,对象直接赋值不起做用
this.$set(a,'title','列表2'); //或者 Vue.set(a,'title','列表2');
不支持响应式的方法
在vue的options中定义filters:{run :function(pram),调用 param|run
关键字:computed
更加高效由于使用了缓存
计算属性的响应式是创建在计算一个响应式的数据上的,它变化才会更新计算属性,而方法是每次都计算不使用缓存
计算属性默认只有 getter ,不过在须要时你也能够提供一个 setter ,给计算属性赋值的时候会调用setter方法,取值调用getter方法
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] } } }
var data = { a: 1 } var vm = new Vue({ el: '#example', data: data }) vm.$data === data // => true vm.$el === document.getElementById('example') // => true // $watch 是一个实例方法 vm.$watch('a', function (newValue, oldValue) { // 此回调函数将在 `vm.a` 改变后调用 })
<input type="text" v-model="message" />{{message}}
<!-- 失去焦点或者enter才会更新值--><input type="text" v-model.lazy="message">{{message}}
每当 data 对象发生变化,都会触发视图从新渲染。值得注意的是,若是实例已经建立,那么只有那些 data
中的本来就已经存在的属性,才是响应式的。也就是说,若是在实例建立以后,添加一个新的属性将不是响应式的
借鉴了将一个大的问题拆分红一个个的小问题这种思想 , 就是"基础库"或者“基础组件",意思是把代码重复的部分提炼出一个个组件供给功能使用。
组件必须放在vue管理的做用域内,若是是多个标签必须被一个元素包裹,就是有一个惟一的祖先元素
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="../../js/vue.js"></script> <div id="app"> <cpt></cpt> <cpt></cpt> <cpt></cpt> <cpt></cpt> </div> <script> // 1. 建立组件构造器 const component = Vue.extend({ template: ` <div> hello </div>`, }); // 2. 注册组件 全局组件 Vue.component('cpt', component); const app = new Vue({ el: "#app", data: { message: "hello world" } }); </script> </body> </html>
<div id="app">11 <cpt></cpt> <cpt></cpt> </div> <div id="app2">22 <cpt></cpt> </div> <script> // 1. 建立组件构造器 const component = Vue.extend({ template: ` <div> hello </div>`, }); //局部组件 只在app中的做用域有效 const app = new Vue({ el: "#app", data: { message: "hello world" }, components: { cpt: component } }); //未注册组件 const app2 = new Vue({ el: "#app2", data: { message: "hello" } }); </script>
<div id="app">11 <pt></pt> <pt></pt> <pt></pt> </div> <script> /*第1个组件构造器*/ const child = Vue.extend({ template: ` <div> child </div>` }); // 第二建立组件构造器 const parent = Vue.extend({ template: ` <div> parent <cd></cd> </div>`, components: { cd: child } }); //局部组件 只在app中的做用域有效 const app = new Vue({ el: "#app", data: { message: "hello world" }, components: { pt: parent } }); </script>
组件不会向上级做用域传递,只会向下传递,孙子没有在爷爷的做用域注册的话孙子只能在父亲的做用域使用
<div id="app">11 <pt></pt> <pt></pt> <pt></pt> </div> <script> //局部组件 只在app中的做用域有效 const app = new Vue({ el: "#app", data: { message: "hello world" }, components: { pt: { // 语法糖直接能够放在注册的地方 template: ` <div> hello </div>` } } }); </script>
<script src="../../js/vue.js"></script> <div id="app">11 <pt></pt> <pt></pt> <pt></pt> </div> <!--<script type="text/x-template" id="pt"> <div> <div>我是标题</div> </div> </script>--> <template id="pt"> <div> <div>我是tempalte</div> </div> </template> <script> //局部组件 只在app中的做用域有效 const app = new Vue({ el: "#app", data: { message: "hello world" }, components: { pt: { // 语法糖直接能够放在注册的地方 template: "#pt" } } }); </script>
<div id="app">11 <pt></pt> <pt></pt> <pt></pt> </div> <template id="pt"> <div> <div>我是{{title}}</div> </div> </template> <script> //局部组件 只在app中的做用域有效 const app = new Vue({ el: "#app", data: { message: "hello world" }, components: { pt: { template: "#pt", //是一个函数,且只能访问本身的数据 data(){ return {title:"title"}; } } } }); </script>
<div id="app"> <pt :msg="msg" :title="title"></pt> </div> <template id="pt"> <div> <div>{{title}}</div> <div>{{msg}}</div> </div> </template> <script> // 1.注册组件 const pt = { template:"#pt", data() { return {}; }, methods: {}, // props:["title","msg"] 能够写成数组或者对象,对象能够限制类型,对象更好点 props:{ // title:Array, title:{ type: Array, default(){ return []; } }, //也能够写成对象的添加更多的限制、给默认值 msg:{ type:String, default:"", required:true, //自定义validator 这个待查阅 validator: function (val) { return val == "hello worl"; } } } } //局部组件 只在app中的做用域有效 const app = new Vue({ el: "#app", data: { msg: "hello world", title:["aaa","bbb","ccc"] }, //字面量简写 pt可替换pt:pt components:{pt} }); </script>
v-on
事件监听器在 DOM 模板中会被自动转换为全小写 (由于 HTML 是大小写不敏感的),因此 v-on:myEvent
将会变成 v-on:myevent
——致使 myEvent
不可能被监听到。<div id="app"> <!-- 不写参数会默认将$emit事件后传的参数【可多个】传出来,写了参数报错--> <pt @child-click="parentClick"></pt> </div> <template id="pt"> <div> <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button> </div> </template> <script> // 1.注册组件 const pt = { template: "#pt", data() { return { categories: [ {id: "aaa", name: "aaa"}, {id: "bbb", name: "bbb"}, {id: "ccc", name: "ccc"}, {id: "ddd", name: "ddd"} ] }; }, methods: { btnClick(ite) { // js中这样写不能驼峰,vue能够 this.$emit('child-click', ite,1); } } }; //局部组件 只在app中的做用域有效 const app = new Vue({ el: "#app", data: { msg: "hello world", title: ["aaa", "bbb", "ccc"] }, components: {pt}, methods: { parentClick(obj,a) { console.log(obj,a); } } }); </script>
<!--1. num一、num2从父组件传递过来 2. 修改num1,dnum1也变,同时传dnum1给父组件,父组件改变num1,也改变了prop1 3. dnum2一直是dnum1的1%--> <div id="app"> <pt :cnum1="num1" :cnum2="num2" @change1="cc1" @change2="cc2" ></pt> </div> <template id="pt"> <div> <p>props:{{cnum1}}</p> <p>data:{{dnum1}}</p> cnum1<input type="text" :value="dnum1" @input="changeProp1"><br> <p>props:{{cnum2}}</p> <p>data:{{dnum2}}</p> cnum2<input type="text" :value="dnum2" @input="changeProp2"> </div> </template> <script> //局部组件 只在app中的做用域有效 const app = new Vue({ el: "#app", data: { num1: 1, num2: 2 }, methods: { cc1(eve1) { this.num1 = eve1; }, cc2(eve2) { this.num2 = eve2; } }, components: { pt: { template: "#pt", props: { cnum1: { type: Number, default: 3 }, cnum2: { type: Number, default: 4 } }, data() { return { dnum1: this.cnum1, dnum2: this.cnum2, }; }, methods: { changeProp1(event1) { this.dnum1 = event1.target.value; console.log(this.dnum1) if (this.dnum1) { this.dnum1 = parseInt(this.dnum1) this.dnum2 = this.dnum1 / 100; this.$emit('change1', this.dnum1); } else { this.dnum2 = ""; } }, changeProp2(event2) { this.dnum2 = event2.target.value; this.$emit('change2', parseInt(this.dnum2)); } } } } }); </script>
watch监听对象不能直接监听,能够用computed代替对象
语法:
watch:{ 监听的属性名(newValue, oldValue){ } }
<script src="../../js/vue.js"></script> <div id="app"> {{message}} <input type="text" v-model="message"> {{demo.name}} <input type="text" v-model="demo.name"> </div> <template id="cd"> <div> aaaaa </div> </template> <script> const app = new Vue({ el: "#app", data: { message: "hello world", demo: { name: "nameObj" } }, computed:{ demoName(){ return this.demo.name; } }, watch: { message(newVal, oldVal) { console.log(newVal, oldVal); }, //不能直接监听对象 // demo(val) { // console.log(val); // } demoName(val) { console.log(val); } }, components: { cd: { template: "#cd" } } }); </script>
若是是键的路径须要用引号包裹
也能够外部调用
immediate和handler
watch有一个特色,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。若是咱们须要在最初绑定值的时候也执行函数,则就须要用到immediate属性。
好比当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也须要执行函数,此时就须要将immediate设为true。
**deep: **当须要监听一个对象的改变时,普通的watch方法没法监听到对象内部属性的改变,只有data中的数据才可以监听到变化,此时就须要deep属性对对象进行深度监听
<div id="app"> {{demo1.name}} <input type="text" v-model="demo1.name"> {{demo.name}} <input type="text" v-model="demo.name"> <input type="text" v-model="demo2"> </div> <script> const app = new Vue({ el: "#app", data: { message: "hello world", demo: { name: "nameObj" }, demo1: { name: "nameObj" }, demo2:"qweqw" }, computed: { demoName() { return this.demo.name; } }, watch: { //若是是键的路径须要用引号包裹 "demo.name": function (val) { console.log(val); }, // childrens: { //监听的属性的名字 // handler:function(val){ // console.log(val.name); // }, // deep: true, //能够监听到一个对象的内部属性变化 // immediate: true // }, // "childrens.name":function (val) { // console.log(val); // } } }); //外部调用 app.$watch("demo2",function (val) { console.log(val) }) </script>
<div id="app"> <tmp ref="a"></tmp> <tmp ref="a"></tmp> <tmp ref="b"></tmp> <button @click="btnClick">打印子组件</button> </div> <template id="tmp"> <div> <p>哈哈哈</p> </div> </template> <script> const app = new Vue({ el: "#app", data: { message: "hello world" }, methods:{ btnClick(){ //1. 通常不会用$children来取子组件 // console.log("第一个子组件:",this.$children[0]); // console.log("全部子组件:",this.$children); // 2.$refs.refName|['refName'] console.log("全部组件有ref属性的组件:",this.$refs); //若是多个相同的引用会取最后一个 console.log("取得固定的ref的元素:",this.$refs["a"]); console.log("取得固定的ref的元素:",this.$refs.b); } }, components: { tmp: { template: "#tmp" } }, }); </script>
<div id="app"> <tmp></tmp> </div> <template id="tmp"> <div> <p>哈哈哈</p> <button @click="btnClick">打印父组件</button> </div> </template> <script> const app = new Vue({ el: "#app", data: { message: "hello world" }, components: { tmp: { template: "#tmp", methods: { btnClick() { //1. 不建议使用,会让组件的耦合加强不够独立 console.log("打印直系父组件:", this.$parent); //祖先组件 console.log("打印root组件:", this.$root); } } }, }, });
<!--1. 插槽的基本使用 <slot></slot>--> <!--2. 插槽的默认值 <slot>默认值</slot>--> <div id="app"> <tmp></tmp><br> <tmp></tmp><br> <tmp></tmp><br> <tmp><div>我是插槽</div></tmp> <tmp><i>我是插槽i</i></tmp> </div> <template id="tmp"> <div> <p>哈哈哈</p> <slot><p>我是默认值*******</p></slot> <p>娃娃</p> </div> </template> <script> const app = new Vue({ el: "#app", data: { message: "hello world" }, components: { tmp: { template: "#tmp" }, } }); </script>
<div id="app"> <tmp ><a slot="right" href="#">我替换右边</a></tmp><br> <tmp ><a slot="left" href="#">我替换左边</a></tmp><br> <tmp><a href="#">我替换没名字的</a></tmp><br> </div> <template id="tmp"> <div> <slot name="left"><p>我是默认值left</p></slot> <slot name="center"><p>我是默认值center</p></slot> <slot name="right"><p>我是默认值right</p></slot> <slot><p>我是默认值没有名字</p></slot> </div> </template> <script> const app = new Vue({ el: "#app", data: { message: "hello world" }, components: { tmp: { template: "#tmp" }, } });
<div id="app"> <!-- 在谁的做用域用谁的变量--> <cp v-show="isShow"></cp> </div> <template id="cp"> <div v-show="isShow"><!-- div父元素初始化的时候不受影响 --> <a href="">aaa</a> <button v-show="isShow">按钮</button> </div> </template> <script> const app = new Vue({ el: "#app", data: { message: "hello world", isShow: true }, components: { cp: { template: "#cp", data() { return { isShow: false }; } } } }); </script>
<!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> <script src="../../js/vue.js"></script> <div id="app"> <cp> <!-- slotData:相似于该组件的对象,2.5以前要用template--> <template slot-scope="slotData"> <!-- 取得绑定在组件中的数据--> <span v-for="item in slotData.datas">{{item}}-</span> </template> </cp> <cp> <template slot-scope="slotData"> <!-- join方法将数组拼接成字符串--> <span>{{slotData.datas.join(' * ')}}</span> </template> </cp> </div> <template id="cp"> <div> <!-- 做为传递的数据--> <slot :datas="languages"> <ul> <li v-for="item in languages">{{item}}</li> </ul> </slot> </div> </template> <script> const app = new Vue({ el: "#app", data: { message: "hello world", }, components: { cp: { template: "#cp", data() { return { languages: ['java', 'javascript', 'css', 'html', 'vb', 'python'] }; } } } }); </script> </body> </html>
把功能进行划分,将同一类型的代码整合在一块儿,因此模块的功能相对复杂,但都同属于一个业务
// ;是为了防止其余的导入js相互影响 ;var xm01 = (function xiaoming01() { return { aa:"asdas", flag: true }; }()) //js文件2 ;(function () { if (xm01.flag) { alert("xm01.flag:" + xm01.flag); } }());
组件化相似模块化的更细粒度,组件充当了基本类库同样的东西目的是复用拓展性,模块主要是以功能区分类别划分尽可能隔离其余业务
xiaoming01.js
// es6的导出,02中导入 export let exa = "222"; let exa1 = "333"; let exb = "333"; export {exb, exa1}; export function fun(){ console.log("asasddsds"); } //export default :import的时候能够自定义命名,一个js中只能有一个default let aaa="export default"; export default aaa;
xiaoming02.js
// 导入 ,这里须要写上.js import {exa, exa1, exb} from "./xiaoming01.js"; // 01 console.log(exa1, exb); //导入default能够自定义命名 import asd from "./xiaoming01.js"; console.log('export:',asd); //导入所有的导出,而且重命名 import * as all from "./xiaoming01.js"; console.log(all); console.log(all.default)
01-es6.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="./xiaoming01.js" type="module"></script> <script src="./xiaoming02.js" type="module"></script> </head> <body> 没有使用导入导出的话: Uncaught ReferenceError: flag is not defined at xiaoming02.js:3 之前是能够执行的先在不知道怎么执行不了了 </body> </html>
node package manager , node包管理工具
查看版本:npm -v
卸载安装的包 npm uninstall 包名 或 npm remove 包名
查看包的详细信息, npm info 包名
查看一个包存在的全部版本号 npm view 包名 versions
查看指定包当前的最新版本 npm view 包名 version
下载指定版本的包 npm install 包名@1.8
npm list 查看项目安装了哪些包 或 npm ls
npm install jquery --save 或 npm i jquery -S 下载生产包
npm install jquery --save-dev 或 npm i jquery -D 下载开发依赖包
npm ls jquery 查看当前安装包的版本
npm config set registry https://registry.npm.taobao.org 更改 npm 的下载镜像为淘宝镜像
npm help npm帮助文档
使用和linux有的很像
*webpack* 是一个现代 JavaScript 应用程序的*静态模块打包器(module bundler)*。当 webpack 处理应用程序时,它会递归地构建一个*依赖关系图(dependency graph)*,其中包含应用程序须要的每一个模块,而后将全部这些模块打包成一个或多个 *bundle*(捆,束),它作的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
入口js
//commonjs规范 const {add} = require('./mathUtil.js'); console.log(add(1,3)); //es6规范 import {result} from "./es6.js"; console.log(result);
es6规范
const result = 45456; export {result};
function add(a, b) { return a + b; } module.exports = {add};
//node的包里面的path模块,用来拼接绝对路径 const path = require('path'); //这里要用commonjs导出,不能用es6 module.exports = { //打包转换的调用入口和main方法相似 entry: './src/main.js', ouput: { //必须使用绝对路径,path.resolve(__dirname,'dist')返回绝对路径 path: path.resolve(__dirname,'dist'), filename: 'bundle.js' } };
{ "name": "meetpackage", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", //npm run build 会在这个配置文件中找webpack命令,这个使用的是本地的命令, //不是全局的webpack,本地是针对于你的这个开发项目 "build":"webpack" }, "author": "", //开发的依赖 "devDependencies": { "webpack": "^3.6.0" }, //开源才须要这个,json中不能注释 "license": "ISC" }
-
-save 会把依赖包名称添加到 package.json 文件 dependencies 下;
-
-save-dev 则添加到 package.json 文件 devDependencies 键下;
//node的包里面的path模块,用来拼接绝对路径 const path = require('path'); //这里要用commonjs导出,不能用es6 module.exports = { entry: './src/main.js', output: { //必须使用绝对路径 path: path.resolve(__dirname,'dist'), filename: 'bundle.js', //为全部的url相关的添加路径 publicPath:'dist/' }, module:{ rules: [ { test: /\.css$/, // style-loader将模块的导出做为样式添加到 DOM 中 // loader解析 CSS 文件后,使用 import 加载,而且返回 CSS 代码 // 从右到左的顺序加载 use: [ 'style-loader', 'css-loader' ] }, // { // test: /\.(png|jpg|gif)$/, // use: [ // { // loader: 'url-loader', // options: { // //限制图片大小,大于limit会找file-loader // limit: 9999 // } // } // ] // }, // 在使用webpack进行打包时,对图片路径的处理方法经常使用的有两种,一种是file-loader, // 一种是url-loader,当咱们使用其中一种是,请把另外一种删掉,否则会出现图片没法正常显示的问题 { test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader', options: { //name是文件名,hash取8位,ext是拓展名 name:'img/[name].[hash:8].[ext]' } } ] }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] } } } ] } };
npm install vue -save
不写路径默认从node_modules引入 import Vue from 'vue'
runtime-only:是运行的时候代码不能包含任意一个template标签
runtime-compiler:代码中能够有template标签
module:{ resolve:{ alias:{ // vue$正则,表示导入的时候会检测vue指向的文件夹,若是这里不指定,会去找默认的runtime-only 'vue$':'vue/dist/vue.esm.js' } }
//使用vue import Vue from 'vue'; const App = { template: ` <h2>{{msg}}</h2> `, data() { return { msg: 'hello world' }; } }; new Vue({ el: '#app', // template和el关系是,这里的template会替换el的标签 template: `<App/>`, components: { App } });
<template> <h2>{{msg}}</h2> <span class="title">{{tit}}</span> </template> <script> export default { name: "App", data() { return { msg: 'hello world', tit:'title' }; } } </script> <style scoped> .title{ color: red; } </style>
全局安装:能够不用
npm install webpack-dev-server -g
开发环境:
npm install webpack-dev-server -save -dev
配置参数:
--content-base //设定webpack-dev-server的director根目录。若是不进行设定的话,默认是在当前目录下。 --quiet: //控制台中不输出打包的信息,开发中通常设置为false,进行 打印,这样查看错误比较方面 --no-info: // 不显示任何信息 --colors: //对信息进行颜色输出 --no-colors: //对信息不进行颜色输出 --compress: //开启gzip压缩 --host <hostname/ip>: //设置ip --port <number>: //设置端口号,默认是:8080 --inline: //webpack-dev-server会在你的webpack.config.js的入口配置文件中再添加一个入口, --hot: //开发热替换 --open: //启动命令,自动打开浏览器 --history-api-fallback: //查看历史url
两种方式:
plugins: [ new webpack.BannerPlugin('最终版权是小明'), //打包静态资源,而且指定模板 new htmlWebpackPlugin({ template:`index.html` }), //压缩js new UglifyJsWebpackPlugin(), //热加载,不会所有加载,只加载改动的地方,配置了hot就须要配置,直接在命令中使用--hot就不须要配置这个插件 // new webpack.HotModuleReplacementPlugin() ], // devServer: { // contentBase: 'src', // port: 80, // hot:true // },
报错多是版本问题
//node的包里面的path模块,用来拼接绝对路径 const path = require('path'); const webpack = require('webpack'); const htmlWebpackPlugin = require('html-webpack-plugin'); const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin'); //这里要用commonjs导出,不能用es6 module.exports = { entry: './src/main.js', output: { //必须使用绝对路径 path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', //为全部的url相关的添加路径 // publicPath: 'dist/' }, module: { rules: [ { test: /\.css$/, // style-loader将模块的导出做为样式添加到 DOM 中 // loader解析 CSS 文件后,使用 import 加载,而且返回 CSS 代码 // 从右到左的顺序加载 use: ['style-loader', 'css-loader'] }, // { // test: /\.(png|jpg|gif)$/, // use: [ // { // loader: 'url-loader', // options: { // //限制图片大小,大于limit会找file-loader // limit: 9999 // } // } // ] // }, // 在使用webpack进行打包时,对图片路径的处理方法经常使用的有两种,一种是file-loader, // 一种是url-loader,当咱们使用其中一种是,请把另外一种删掉,否则会出现图片没法正常显示的问题 { test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader', options: { //name是文件名,hash取8位,ext是拓展名 name: 'img/[name].[hash:8].[ext]' } } ] }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] } } }, { test: /\.vue$/, use: { loader: 'vue-loader' } } ] }, resolve: { // 这写拓展名能够省略 extensions: ['.css', '.js', '.vue'], alias: { // vue$正则,表示导入的时候会检测vue指向的文件夹,若是这里不指定,会去找默认的runtime-only 'vue$': 'vue/dist/vue.esm.js' } }, plugins: [ new webpack.BannerPlugin('最终版权是小明'), //打包静态资源,而且指定模板 new htmlWebpackPlugin({ template:`index.html` }), //压缩js new UglifyJsWebpackPlugin(), //热加载,不会所有加载,只加载改动的地方,配置了hot就须要配置,直接在命令中使用--hot就不须要配置这个插件 // new webpack.HotModuleReplacementPlugin() ], // devServer: { // contentBase: 'src', // port: 80, // hot:true // }, };
//node的包里面的path模块,用来拼接绝对路径 const path = require('path'); const webpack = require('webpack'); const htmlWebpackPlugin = require('html-webpack-plugin'); //这里要用commonjs导出,不能用es6 module.exports = { entry: './src/main.js', output: { //必须使用绝对路径 path: path.resolve(__dirname, '../dist'), filename: 'bundle.js', }, module: { rules: [ { test: /\.css$/, // style-loader将模块的导出做为样式添加到 DOM 中 // loader解析 CSS 文件后,使用 import 加载,而且返回 CSS 代码 // 从右到左的顺序加载 use: ['style-loader', 'css-loader'] }, { test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader', options: { //name是文件名,hash取8位,ext是拓展名 name: 'img/[name].[hash:8].[ext]' } } ] }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['es2015'] } } }, { test: /\.vue$/, use: { loader: 'vue-loader' } } ] }, resolve: { // 这写拓展名能够省略 extensions: ['.css', '.js', '.vue'], alias: { // vue$正则,表示导入的时候会检测vue指向的文件夹,若是这里不指定,会去找默认的runtime-only 'vue$': 'vue/dist/vue.esm.js' } }, plugins: [ new webpack.BannerPlugin('最终版权是小明'), //打包静态资源,而且指定模板 new htmlWebpackPlugin({ template: `index.html` }) ], };
dev.config.js : 存放开发时配置
const WebpackMerge = require('webpack-merge'); const baseConfig = require('./base.config'); module.exports = WebpackMerge(baseConfig, { devServer: { contentBase: 'src', port: 80, inline: true } });
prod.config.js : 存放生产时配置
const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin'); const WebpackMerge = require('webpack-merge'); const baseConfig = require('./base.config'); module.exports = WebpackMerge(baseConfig, { plugins: [ //压缩js new UglifyJsWebpackPlugin() ] });
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack-dev-server --config ./build/dev.config.js", "build": "webpack --config ./build/prod.config.js" },
使用webpack,也能够不用配置文件本身在命令后指定参数,如下是使用配置文件
使用npm,会找到package.json找到对应的script里的命令执行,实际上仍是调用了webpack命令
介绍:
安装:
卸载以前版本
拉取v2的模板
npm install -g @vue/cli-init
vue init webpack projectName
零配置
隐藏build和config目录,能够在node-modules/@vue/cli-service
要修改配置须要根目录建立一个vue.config.js
module.exports={};
基于webpack4
提供vue ui命令,图形化操做
移除static,新增public目录将index.html移动到下面
项目名不能有大写
vue create projectName
会默认建立一个.git文件夹
自定义配置:
module.exports = { configureWebpack: { resolve: { // extensions:[], //配置别名 alias: { 'assets': '@/assets', 'components': '@/components', 'network': '@/network', 'common': '@/commom', 'views': '@/views', } } } };
root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true
vue ui 打开图形管理界面
runtime-only:是运行的时候代码不能包含任意一个template标签
runtime-compiler:代码中能够有template标签
template加载过程:
template - 》parse - 》ast 抽象语法树 - 》compiler - 》render(h)- 》 virtual Dom - 》UI真实dom
1比2性能更高,代码更少(少6kb)
//runtime-compiler // 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' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', components: { App }, template: '<App/>' }) import Vue from 'vue' import App from './App' Vue.config.productionTip = false //runtime-only,这个h是一个createElement('tagName',{attrName:'attrVal'},['innerHtml']) //在vue中也能够传一个template对象靠vue-template-compiler解析成render(),也能够递归建立 /* eslint-disable no-new */ new Vue({ el: '#app', render: h => h(App) })
通常使用vue的插件都要用Vue.use(插件)
安装路由:npm install vue-router --save 由于生产也须要路由
导入:
import Vue from 'vue' //1. 导入插件 import Router from 'vue-router' import HelloWorld from '@/components/HelloWorld' //2. 使用插件 Vue.use(Router) //3. 建立路由配置 const routes = [ { path: '/', name: 'HelloWorld', component: HelloWorld } ]; //4. 传入路由配置,导出路由对象 export default new Router({ routes })
import Vue from 'vue' import App from './App' //只写目录默认会找 index.js import router from './router' Vue.config.productionTip = false new Vue({ el: '#app', router, render: h => h(App) })
<div id="app"> <router-link to="/home">首页</router-link> <!-- 至关于占位符 --> <router-view></router-view> <router-link to="/about">详情</router-link> </div>
经常使用属性
tag 、replace
<!-- tag设置替换成什么标签 --> <!-- replace表示禁用了返回前进按钮,是使用了history.replaceState() --> <router-link to="/home" tag='button' replace>首页</router-link>
配置默认的active的样式
.router-link-active{ color: #f00 }
自定义样式:手动一个一个标签的写
<!--active-class 自定义点击后的样式 --> <router-link to="/home" tag='button' replace active-class="active">首页</router-link>
配置全局的active-class
export default new Router({ routes, mode:'history', linkActiveClass:'active' })
const routes = [ { path:'/', redirect:'/home' }, { path: '/home', name: 'Home', component: Home }, { path:'/about', name:'About', component:About } ];
//4. 传入路由配置,导出路由对象 export default new Router({ routes, mode:'history' })
<template> <div id="app"> <button @click="homeClick">首页</button> <button @click="aboutClick">详细</button> <router-view></router-view> </div> </template> <script> export default { name: 'App', methods:{ //router会给每一个组件传$router homeClick(){ // this.$router.push('/home'); this.$router.replace('/home'); }, aboutClick(){ // this.$router.push('/about'); this.$router.replace('/about'); } } } </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>
建立一个vue组件:User.vue
<template> <div> <h2>我的信心</h2> <h3></h3> </div> </template> <script> export default { name:'User', } </script> <style> </style>
配置路由:index.js
import Vue from 'vue' import User from '../components/User.vue' //1. 导入插件 import Router from 'vue-router' //2. 使用插件 Vue.use(Router) //3. 建立路由配置 const routes = [ { path:'/user', component:User } ]; //4. 传入路由配置,导出路由对象 export default new Router({ routes, mode:'history', linkActiveClass:'active' })
加入路由到目标组件:Vue.vue
<template> <div id="app"> <router-link to="/user" replace>用户</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App', } </script> <style> .active{ color: rgb(209, 15, 25) } </style>
导入组件到入口 : main.js
import Vue from 'vue' import App from './App' //只写目录默认会找 index.js import router from './router' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, render: h => h(App) })
设置动态路由:index.js
import Vue from 'vue' import User from '../components/User.vue' //1. 导入插件 import Router from 'vue-router' //2. 使用插件 Vue.use(Router) //3. 建立路由配置 const routes = [ { path:'/user/:userName', component:User } ]; //4. 传入路由配置,导出路由对象 export default new Router({ routes, mode:'history', linkActiveClass:'active' })
配置页面的url: Vue.vue
<template> <div id="app"> <router-link v-bind:to="'/user/'+userName" replace>用户</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App', data(){ return { userName:'xiaoming' } } } </script> <style> .active{ color: rgb(209, 15, 25) } </style>
获取动态路由中的参数:User.vue
<template> <div> <h2>我的信心</h2> <h3>{{userName}}</h3> </div> </template> <script> export default { name:'User', computed:{ userName(){ return this.$route.params.userName; } } } </script> <style> </style>
路由配置中props
被设置为 true
,route.params
将会被设置为组件属性。
编程式路由
// 字符串 router.push('home') // 对象 router.push({ path: 'home' }) // 命名的路由 router.push({ name: 'user', params: { userId: '123' }}) // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }})
注意: path和params 不共存params会被忽略,path和query 能够。name和params也能够
const userId = '123' //命名路由 router.push({ name: 'user', params: { userId }}) // -> /user/123 router.push({ path: `/user/${userId}` }) // -> /user/123 // 这里的 params 不生效 router.push({ path: '/user', params: { userId }}) // -> /user
一样的规则也适用于 router-link
组件的 to
属性
有时候,同一个路径能够匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。
若是把全部的js都打包到app中,js将会很大,访问的时候会有等待时间,因此把不一样的路由对应的组件分割成不一样的代码块,而后当路由被访问的时候加载对应的资源,就更加高效了
import Vue from 'vue' //替换成懒加载 // import Home from '../components/Home.vue' // import About from '../components/About.vue' // import User from '../components/User.vue' //懒加载: const Home = ()=>import('../components/Home.vue') const About = ()=>import('../components/About.vue') const User = ()=>import('../components/User.vue') //1. 导入插件 import Router from 'vue-router' //2. 使用插件 Vue.use(Router) //3. 建立路由配置 const routes = [ { path:'/', redirect:'/home' }, { path: '/home', name: 'Home', component: Home }, { path:'/about', name:'About', component:About }, { path:'/user/:userName', component:User } ]; //4. 传入路由配置,导出路由对象 export default new Router({ routes, mode:'history', linkActiveClass:'active' })
import Vue from 'vue' //替换成懒加载 // import Home from '../components/Home.vue' // import About from '../components/About.vue' // import User from '../components/User.vue' //懒加载: const Home = () => import('../components/Home.vue') const About = () => import('../components/About.vue') const User = () => import('../components/User.vue') const HomeChild = () => import ('../components/HomeChild.vue') //1. 导入插件 import Router from 'vue-router' //2. 使用插件 Vue.use(Router) //3. 建立路由配置 const routes = [ { path: '/', redirect: '/home' }, { path: '/home', name: 'Home', component: Home, children: [ { path: '', // redirect:'child' }, { //这里不能同/开头,会自动加上 path: 'child', name: 'HomeChild', component: HomeChild }] }, { path: '/about', name: 'About', component: About }, { path: '/user/:userName', component: User } ]; //4. 传入路由配置,导出路由对象 export default new Router({ routes, mode: 'history', linkActiveClass: 'active' })
<template> <div> <h2>首页11</h2> <router-link to="/home/child">child</router-link> <router-view></router-view> </div> </template> <script> export default { name:'Home' } </script> <style> </style>
<template> <div><span>我的档案</span> <span>{{$route.query}}</span><br> <span>query.name: {{$route.query.name}}</span> </div> </template> <script> export default { name: "Profile" } </script> <style scoped> </style>
const Profile = () => import('../components/Profile') { path: '/profile', component: Profile }
<template> <div id="app"> <router-link to="/home" tag='button' replace >首页</router-link> <router-link to="/about" replace>详情</router-link> <router-link :to="'/user/'+userName" replace>用户</router-link> <router-link :to="{path:'/profile',query:{name:'lisa',age:18},fragment:'4d5as46s'}" replace>档案</router-link> <!-- <button @click="homeClick">首页</button> <button @click="aboutClick">详细</button> --> <router-view></router-view> </div> </template>
注意导航守卫并无应用在跳转(redirect)路由上,而仅仅应用在其目标上。为redirect的路由添加一个 beforeEach
或 beforeLeave
守卫并不会有任何效果
全部的路由都会被过滤,也能够在特定的组件内建立局部守卫
主要监听页面的跳转
from从哪一个组件来的
to去跳转到哪一个组件
next()
import Vue from 'vue' //替换成懒加载 // import Home from '../components/Home.vue' // import About from '../components/About.vue' // import User from '../components/User.vue' //懒加载: const Home = () => import('../components/Home.vue') const About = () => import('../components/About.vue') const User = () => import('../components/User.vue') const HomeChild = () => import('../components/HomeChild.vue') const Profile = () => import('../components/Profile') //1. 导入插件 import Router from 'vue-router' //2. 使用插件 Vue.use(Router) //3. 建立路由配置 const routes = [ { path: '/', redirect: '/home' }, { path: '/home', name: 'Home', component: Home, meta: { title: '首页' }, children: [ { path: '', // redirect:'child' }, { //这里不能同/开头,会自动加上 path: 'child', name: 'HomeChild', component: HomeChild, }] }, { path: '/about', name: 'About', component: About, meta: { title: '详情' }, }, { path: '/user/:userName', component: User, meta: { title: '用户' }, }, { path: '/profile', component: Profile, meta: { title: '档案' }, } ]; const router = new Router({ routes, mode: 'history', linkActiveClass: 'active' }) router.beforeEach((to, from, next) => { next() //匹配path中的meta对象的title document.title = to.matched[0].meta.title console.log(to); // console.log(from); // console.log("next: "+next); }) //4. 传入路由配置,导出路由对象 export default router
import Vue from 'vue' //懒加载: const Home = () => import('../components/Home.vue') //1. 导入插件 import Router from 'vue-router' //2. 使用插件 Vue.use(Router) //3. 建立路由配置 const routes = [ { path: '/', redirect: '/home' }, { path: '/home', name: 'Home', component: Home, meta: { title: '首页' }, children: [ { path: '', // redirect:'child' }, { //这里不能同/开头,会自动加上 path: 'child', name: 'HomeChild', component: HomeChild, beforeEnter: (to, from, next) => { console.log("独享守卫"); next() } }] } ]; const router = new Router({ routes, mode: 'history', linkActiveClass: 'active' }) //4. 传入路由配置,导出路由对象 export default router
import Vue from 'vue' //懒加载: const Home = () => import('../components/Home.vue') //1. 导入插件 import Router from 'vue-router' //2. 使用插件 Vue.use(Router) //3. 建立路由配置 const routes = [ { path: '/', redirect: '/home' }, { path: '/home', name: 'Home', component: Home, meta: { title: '首页' } }, ]; const router = new Router({ routes, mode: 'history', linkActiveClass: 'active' }) //前置钩子 hook,像filter同样 router.beforeEach((to, from, next) => { next() //匹配path中的meta对象的title document.title = to.matched[0].meta.title console.log(to); }) //后置钩子 router.afterEach((to,from)=>{ console.log("在跳转以后调用"); }) //4. 传入路由配置,导出路由对象 export default router
是vue的一个组件:保证一些组件进入缓存不用你每次请求解析资源,提升效率,在显示的地方配置
<template> <div id="app"> <router-link to="/home" tag="button" replace>首页</router-link> <router-link to="/about" replace>详情</router-link> <router-link :to="'/user/'+userName" replace>用户</router-link> <router-link :to="{path:'/profile',query:{name:'lisa',age:18},fragment:'4d5as46s'}" replace>档案</router-link> <button @click="toProfile">档案2</button> <!-- <button @click="homeClick">首页</button> <button @click="aboutClick">详细</button>--> <!-- <router-view></router-view> --> <!-- 保存到缓存中 --> <keep-alive> <router-view></router-view> </keep-alive> </div> </template>
keep-alive的组件才可使用activated()、deactivated()
<template> <div> <h2>首页11</h2> <router-link :to="{path:'/home/child',query:{content:'child1'}}">child</router-link> <router-link :to="toChild2">child2</router-link> <router-view></router-view> </div> </template> <script> export default { name: "Home", data() { return { toChild2: { path: "/home/child2", query: { content: "child2" } }, path: "/home/child", query:{ childContent:'child1' } }; }, methods: {}, created() { console.log("Home组件被建立成功"); }, mounted() { console.log("组件被挂载成功"); }, updated() { console.log("组件中发生改变时"); }, destroyed() { console.log("home destroyed"); }, activated() { console.log("home 激活"); this.$router.push(this.path) }, deactivated() { console.log("home 离开"); }, beforeRouteLeave(to, from, next) { console.log('before leave home'); this.path = this.$route.path; console.log(this.path); next(); } }; </script> <style> </style>
keep-alive 的exclude、include属性
<keep-alive exclude="Profile"> <router-view></router-view> </keep-alive>
export default { name: "Profile", created() { console.log("profile created"); }, destroyed() { console.log("profile destroyed"); } };
/*style中引用要用@import */
准备好tabbar.vue,调好样式,预留出来一个插槽,用来放具体的tabbar的item
<template> <div id="tab-bar"> <slot></slot> </div> </template> <script> export default { name: "TabBar", } </script> <style scoped> #tab-bar { display: flex; background-color: #fdfdff; /*显示在最下面和屏幕等宽*/ position: fixed; left: 0; right: 0; bottom: 0; /*阴影 fgba(最后是0.1表示透明度)*/ box-shadow: 0 -1px 1px rgba(100, 100, 100, .1); } </style>
封装tab-bar-item
<template> <div class="tab-bar-item" @click="itemClick"> <div v-if="!isActive"> <slot name="item-icon"></slot> </div> <div v-else> <slot name="item-icon-active"></slot> </div> <div :class="{active:isActive}"> <slot name="item-text"></slot> </div> </div> </template> <script> export default { name: "TabBarItem", props:{ path:{ type:String } }, data() { return { // isActive: true } }, computed:{ isActive(){ return this.$route.path.indexOf(this.path) !== -1 } }, methods:{ itemClick(e){ this.$router.replace(this.path) } } } </script> <style scoped> .tab-bar-item { flex: 1; text-align: center; /*通常移动端的tabbar都是49px*/ height: 49px; font-size: 14px; } .tab-bar-item img { width: 24px; height: 24px; margin-top: 3px; margin-bottom: 2px; /*能够去掉图片下面的三个像素*/ vertical-align: bottom; } .active { color: red; } </style>
注册到app.vue中
<template> <div id="app"> <router-view></router-view> <tab-bar> <tab-bar-item path="/home"> <img slot="item-icon" src="./assets/images/tabbar/home.png" alt="首页"> <img slot="item-icon-active" src="./assets/images/tabbar/home_active.png" alt=""> <div slot="item-text">首页</div> </tab-bar-item> <tab-bar-item path="/category"> <img slot="item-icon" src="./assets/images/tabbar/category.png" alt=""> <img slot="item-icon-active" src="./assets/images/tabbar/category_active.png" alt=""> <div slot="item-text">分类</div> </tab-bar-item> <tab-bar-item path="/cart"> <img slot="item-icon" src="./assets/images/tabbar/cart.png" alt=""> <img slot="item-icon-active" src="./assets/images/tabbar/cart_active.png" alt=""> <div slot="item-text">购物车</div> </tab-bar-item> <tab-bar-item path="/profile"> <img slot="item-icon" src="./assets/images/tabbar/profile.png" alt=""> <img slot="item-icon-active" src="./assets/images/tabbar/profile_active.png" alt=""> <div slot="item-text">个人</div> </tab-bar-item> </tab-bar> </div> </template> <script> import TabBar from "./components/tabbar/TabBar"; import TabBarItem from "./components/tabbar/TabBarItem"; export default { name: 'App', components: { TabBar, TabBarItem } } </script> <style> /*style中引用要用@*/ @import "./assets/css/base.css"; </style>
能够优化class,颜色直接写死不合适
还能够从父组件传过来,而后绑定style来设置
配置别名:\build\webpack.base.conf.js
resolve: { extensions: ['.js', '.vue', '.json'], alias: { '@': resolve('src'), 'assets': resolve('src/assets'), 'components': resolve('src/components'), 'views': resolve('src/views'), } },
项目中使用
html中: 前面要加 ~
<tab-bar-item path="/home" activeColor="blue"> <img slot="item-icon" src="~assets/images/tabbar/home.png" alt="首页" /> <img slot="item-icon-active" src="~assets/images/tabbar/home_active.png" alt /> <div slot="item-text">首页</div> </tab-bar-item>
import中使用
import TabBarItem from "components/tabbar/TabBarItem";
根目录下新建vue.config.js
在vue.config.js中的chainWebpack中配置config.resolve.alias.set('@', resolve('src')).set('components', resolve('src/components'));
是异步编程的一种解决方案
new Promise((resolve, reject) => { setTimeout(() => { resolve('传入then 中的 data') }, 1500) }).then(data => { console.log(data); return new Promise((resolve, reject) => { setTimeout(() => { // resolve('内部的resolve') reject('内部的reject') }, 1500) }) }).catch(data => { console.log(data); })
promise异步完成后会有三种状态
promise的另外一种写法
new Promise((resolve, reject) => { setTimeout(() => { resolve('传入then 中的 data') // reject('失败') }, 1500) }).then(data => { console.log(data); },reject => { console.log(reject); })
再简化
// new Promise(resolve) ==>Promise.resolve(data) ==> data //throw 'msg'也会被catch()捕获 new Promise((resolve, reject) => { setTimeout(() => { resolve('第一层...') }, 1500) }).then(data => { console.log(data); return Promise.resolve('第二层...') // return Promise.reject('额鹅鹅鹅') throw 'dsadsa' }).then(data=>{ console.log(data); return 'aaa' }).then(data=>{ console.log(data); }).catch(err=>{ console.log(err); })
Promise.all([ new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('1111111') },1000) }), new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve('222222') },2000) }) ]).then(data=>{ //1111111,222222 console.log(data.toString()) })
经过提交 mutation 的方式,而非直接改变 store.state.count
,是由于咱们想要更明确地追踪到状态的变化。这个简单的约定可以让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让咱们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,咱们甚至能够实现如时间穿梭般的调试体验。
因为 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅须要在计算属性中返回便可。触发变化也仅仅是在组件的 methods 中提交 mutation。
actions步骤能够省略,通常异步的操做放在actions中完成后放在mutations中
mutations只能是同步的操做,devtools监听不到异步操做
store用法
state中全部的已定义的属性都是响应式的,新加入的不被响应:由于属性初始化后,都被一个dep对象=【watcher,watcher..】监控,后面加入的不受监控
npm install vuex --save
新建、src/store/index.js
import Vue from 'vue' import Vuex from 'vuex' //1.安装,底层会调用Vuex.install Vue.use(Vuex) // 2.建立对象 const store = new Vuex.Store({ state: { count: 0 }, mutations: { //state必须传,默认会传进来 increment(state) { state.count++ } }, actions: {}, getters: {}, modules: {} }) // 3.导出store对象 export default store
import Vue from 'vue' import App from './App' import store from "./store"; Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', store, render: h => h(App) })
<template> <div id="app"> <h2>{{$store.state.count}}</h2> <button @click="increment">+</button> <hello-vuex></hello-vuex> </div> </template> <script> import HelloVuex from "./components/HelloVuex"; export default { name: 'App', methods:{ increment(){ this.$store.commit('increment') } }, components: { HelloVuex } } </script> <style> </style>
<template> <div> <h2>{{$store.state.count}}</h2> </div> </template> <script> export default { name: "HelloVuex" } </script> <style scoped> </style>
有点像computed的概念
<template> <div id="app"> <h2>{{$store.state.count}}</h2> <button @click="increment">+</button> <h2>年龄大于20:{{$store.getters.more20Person}}</h2> <h2>年龄大于20个数:{{$store.getters.more20PersonCount}}</h2> <h2>年龄大于age个数:{{$store.getters.moreAgePerson(13)}}</h2> <hello-vuex></hello-vuex> </div> </template> <script> import HelloVuex from "./components/HelloVuex"; export default { name: 'App', methods:{ increment(){ this.$store.commit('increment') } }, components: { HelloVuex } } </script> <style> </style>
import Vue from 'vue' import Vuex from 'vuex' //1.安装,底层会调用Vuex.install Vue.use(Vuex) // 2.建立对象 const store = new Vuex.Store({ state: { count: 0, persons: [ {name: 'a', age: 12}, {name: 'b', age: 23}, {name: 'c', age: 32}, {name: 'd', age: 24} ] }, mutations: { //state必须传,默认会传进来 increment(state) { state.count++ } }, actions: {}, //最多只能写两个参数时state,getters,默认会传进来 getters: { more20Person(state) { return state.persons.filter(per=>per.age>20) }, more20PersonCount(state,getters){ // 这里不用写括号 return getters.more20Person.length }, //返回一个函数能够传动态的参数 moreAgePerson(state){ return (age)=>{ return state.persons.filter(per=>per.age>age) } } }, modules: {} }) // 3.导出store对象 export default store
//store/index.js mutations: { //state必须传,默认会传进来 increment(state) { state.count++ }, add(state,num){ state.count +=num } } //app.vue methods:{ increment(){ this.$store.commit('increment') }, add(num){ this.$store.commit('add',num) } }
第二种提交风格
inc(num){ this.$store.commit({ type:'inc', num }) }
mutations: { //state必须传,默认会传进来 increment(state) { state.count++ }, add(state,num){ state.count +=num }, //当成对象处理参数 inc(state,payLoad){ state.count +=payLoad.num } }
update(state){ //响应式 // state.persons.push({name:'e',age:99}) //响应式 // state.person={name:'f',age:101} //新加的属性不会被监控,只有在其余任意的属性变化一次后他会刷新一次 // state.person.add=111 // state.person['address']=222 //删除一个对象的属性 // delete state.person.age //vue set value是响应式的,key必须是字符串 // Vue.set(state.person,'asd','vue set value是响应式的') Vue.delete(state.person,'age') }
避免写错,定义一个常量对象,在使用的文件中导入
定义
[const](){}
//mutation-type.js export const INCREMENT='increment' export const ADD='add' export const INC='inc' export const UPDATE='update' import {INCREMENT,ADD,UPDATE,INC} from "./mutation-type"; //app.vue update(){ this.$store.commit({ type:UPDATE, }) } //index.js mutations: { //state必须传,默认会传进来 [INCREMENT](state) { state.count++ }, [ADD](state,num){ state.count +=num }, //当成对象处理参数 [INC](state,payLoad){ state.count +=payLoad.num }, [UPDATE](state){ Vue.delete(state.person,'age') } }
action处理异步操做:
//app.vue aUpdate(){ // this.$store.dispatch('aUpdate',{ // msg:'参数信息', // success:(data)=>{console.log(data)} // }) //第二种方法,异步函数返回的promise对象 this.$store.dispatch('aUpdate',{ msg:'参数信息' }).then(res=>{ console.log('完成异步操做'); console.log(res); }) } //index.js actions: { // aUpdate(context,payload) { // // console.log('默认参数是上下文对象: ',context) // setTimeout(function () { // context.commit('aUpdate',payload) // }, 1000) // } //第二种方式返回一个promise对象,在调用处可使用 aUpdate(context, payload) { return new Promise((resolve, reject) => { setTimeout(() => { context.commit('aUpdate', payload) resolve(12312) }, 1000) }) } }
显示:app.vue
<h2>-------------state--modules的内容---------</h2> <h2>{{$store.state.a.name}}</h2> <h2>{{$store.getters.getModuleA}}</h2> <h2>{{$store.getters.getModuleA_add('age')}}</h2> <h2>{{$store.getters.getModuleA_add_root}}</h2> <button @click="moduleA">模块a修改name</button> <button @click="asyncUpdateModuleA">异步模块a修改name</button> methods:{ moduleA() { this.$store.commit('aUpdate','模块a名字修改') }, asyncUpdateModuleA(){ this.$store.dispatch('asyncUpdateModuleA') } }
index.js
modules: { a:{ //须要指定模块名,能够和父模块同名 state:{name:'module_a',person:123}, //和父模块同名会报错,能够直接访问不须要指定模块名 getters:{ getModuleA(state){ return state.name+'_getModuleA' }, getModuleA_add(state,getters){ return (age) => { return getters.getModuleA+age } }, //三个默认参数 getModuleA_add_root(state,getters,rootState){ return state.name+getters.getModuleA+'_add_'+rootState.count } }, // 和mutations使用差很少 actions:{ //也可使用对象的解构,详见es6 asyncUpdateModuleA(context){ setTimeout(()=>{ context.commit('aUpdate','异步修改子模块') },1000) } }, mutations:{ //和父模块名字相同都会调用,先调用父模块的,因此不要定义相同的名字 aUpdate(state,payload){ state.name=payload console.log('child mutations 被调用') } }, modules:{} }, //模块b b:ModuleB }
import Vue from 'vue' import Vuex from 'vuex' import mutations from "./mutations"; import actions from "./actions"; import getters from "./getters"; import module_a from "./modules/module_a"; //1.安装,底层会调用Vuex.install Vue.use(Vuex) // 2.建立对象 const store = new Vuex.Store({ state: { count: 0, persons: [ {name: 'a', age: 12}, {name: 'b', age: 23}, {name: 'c', age: 32}, {name: 'd', age: 24} ], person: { name: 'g', age: 100 } }, mutations, actions, getters, modules: { a: module_a } }) // 3.导出store对象 export default store
export default new Vuex.Store({ state:sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')): { enterprise: {}, grid: [], iofficeUserRoleID: '', userVO: {}, },
mounted() { window.addEventListener('unload', this.saveState) }, methods: { saveState() { sessionStorage.setItem('state', JSON.stringify(this.$store.state)) } }
import axios from 'axios' axios.defaults.baseURL = 'https://httpbin.org' axios.defaults.timeout = 5000 axios({ // url:'http://123.207.32.32:8080/home/mutidata', url: 'post', method: 'post', // 拼接在URL后 params: { name: 1 }, // 请求体中的参数 data: { type: 'sell', page: 3 }, //拦截请求 transformRequest:[function (query) { }], //拦截返回数据 transformResponse:[function (response) { }], }).then(res => { console.log(res); }) // 同时处理多个异步请求,最后返回一个数据数组,像java的栅栏 axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})]).then(res => { console.log(res); }) //处理返回的结果数组,使用的是数组的解构是根据下标解构的 axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})]) .then(([res1, res2]) => { console.log(res1); console.log(res2); }) // 这样也能够 axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})]) .then( axios.spread((res1, res2) => { console.log(res1); console.log(res2); }))
避免使用全局的axios,可能每一个模块的请求是不同的
新建/network/request.js
import axios from "axios"; export function request(config) { if (!config.baseURL) { config.baseURL = 'https://httpbin.org' } if (!config.timeout) { config.timeout = 5000; } const axiosInstance = axios.create(config); //req是请求参数对象 axiosInstance.interceptors.request.use(req => { console.log(req); //1.能够修改一些请求的参数 // 2.能够设置一个加载图片 return req }) //res是返回的对象 axiosInstance.interceptors.response.use(res => { console.log(res.data); return res.data }) return axiosInstance(config); }
只会取当前模块的引用
只会做用当前的组件的css
false
以阻止 vue 在启动时生成生产提示。:style 后面是对象的时候里面的属性值是字符串格式
controller
@RequestMapping("save") public ResponseModel savenew(@RequestBody @Validated SysUser user, BindingResult result) { if (result.hasErrors()) { return ResponseModel.FAIL() .setMsg(result.getAllErrors() .stream() .map(err->err.getDefaultMessage()) .collect(Collectors.joining(";")) ); } String password = user.getPassword(); if (password.length() < 32) { user.setPassword(CryptUtil.shiroEncry(user)); } userService.save(user); return ResponseModel.SUCCESS(); }
vue
<template> <div id="user"> <div> 姓名: <input type="text" name="username" v-model="entity.username"/> </div> <div> 密码: <input type="password" v-model="entity.password" /> </div> <div> 电话: <input type="text" v-model="entity.phone" /> </div> <div> 电话: <input type="text" v-model="entity.role.roleName" /> </div> <button @click="saveUserInfo">保存</button> </div> </template> <script> import { saveUser } from 'network/module/user.js'; export default { name: "User", methods: { saveUserInfo() { saveUser(this.entity).then(res=>alert(res.msg)); } }, data(){ return { entity:{ role:{} } } } }; </script>
前端工具请求接口的类型和后端服务器定义的类型不一致形成
vue.config.js
//vue-cli3配置这个作代理 devServer: { proxy: { '/api': { target: 'http://localhost:8080/mall/api', //API服务器的地址 changeOrigin: true, // 是否跨域 pathRewrite: { '^/api': '' } } }, }, //vue-cli2使用这个 // dev: { // proxyTable: { // '/api': { // target: 'http://localhost:8080/mall/api', //API服务器的地址 // changeOrigin: true, // 是否跨域 // pathRewrite: { // '^/api': '' // } // } // } // },
手写嵌套对象:
{ a:{ 'b.c':d } }
qs默认嵌套对象的序列化会用 ‘[]’
//{ allowDots: true }会将【】转换成 ‘.’ this.$qs.stringify(obj,{ allowDots: true })
~1.15.2 := >=1.15.2 <1.16.0 匹配到第二位,大于当前版本小于1.16.0
^3.3.4 := >=3.3.4 <4.0.0 匹配到第一位全部大于当前版本且小于4
this.$nextTick(function)将回调延迟到下次 DOM 更新循环以后执行,vm.$nextTick()
实例方法特别方便,由于它不须要全局 Vue
,而且回调函数中的 this
将自动绑定到当前的 Vue 实例上