MVVM设计思想

  • M(model)
  • V(view)
  • VM(View-Model)

Vue生命周期

  • 挂载(初始化相关属性)

    • beforeCreate->在实力初始化之后,数据观测和配置之前被调用
    • created->在实力创建完成后被立即调用
    • beforeMount->在挂载开始之前被调用
    • mounted->被新创建的vm.$el替换,并挂载到实力上之后调用钩子
  • 更新(元素或组件的变更操作)
    • beforeUpdate->数据更新时调用,发生在虚拟DOM打补丁之前(调用后台接口向模板中填充数据)
    • updated->由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该狗子
  • 销毁(销毁相关属性)
    • beforeDestroy->实例销毁之前调用
    • destroyed->实例销毁后调用

数据交互方式:

视图层->监听dom->数据层

数据层->根据数据绑定->视图层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YYIQxm7Z-1596001043673)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-26 上午9.30.20.png)]

Vue->helloWorld

<div>{{msg}}
</div>
new Vue({el:'',data:{msg:'1'}
})
el:元素的挂载位置
data:模型数据
{{}}:差值表达式->将数据填充到HTML标签中,支持基本的计算操作。

Vue 指令

自定义的属性:在标签上定义的data-xxxx/abc = ’ ';

v-cloak 指令

作用:解决差值表达式闪动问题
1.提供样式

[v-cloak]{display:none;
}

2.在差值表达式所在的标签中添加v-cloak

2.在差值表达式所在的标签中添加v-cloak

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EkZPV8XF-1596001043675)(/Users/limboaaa/Desktop/截屏2020-07-26 上午8.54.22.png)]

v-text 指令

描述:向页面中插入一个纯文本

v-html 指令

描述:填充HTML片段

  • 存在安全问题
  • 本网站内部数据可以使用,来自第三方的数据不可用

v-pre 指令

**描述:**显示原始信息,跳过编译过程

v-once 指令

**描述:**只编译一次,显示内容后不再具有数据响应式功能

应用场景:显示的信息后续不必再修改,可以使用v-once,可以提高性能。

<div v-once>{{msg}}</div>

v-model 指令(双向数据绑定)

<input type="text" v-model= 'msg'>

v-model的本质,就是v-bind:value = ‘’,然后Input监听事件,将input的内容复制给该属性;

<input type="text" v-model= 'msg' @input = 'msg = $event.target.value'>

事件绑定

v-on指令

完全体:
<button v-on:click='num++'>点击</button>
简写:
<button @click='num++'>点击</button>

.stop 阻止冒泡

    <div id="app">{{num}}<div @click = 'handle'><button @click.stop='handle1'>点击1</button></div></div>

.prevent 阻止默认行为

   <a href="https://www.baidu.com" @click.prevent='handle'>跳转至百度</a>

键盘:enter事件/ delete事件

用户名:<input type="text" v-model='name' @keyup.delete = 'delAll'>
密码:<input type="text"  v-model='pwd' @keyup.enter = 'submit'>

自定义按键修饰符

全局变量:config.keyCodes对象
Vue.config.keyCodes.xx = keynum;
使用:<input type="text"  v-model='pwd' @keyup.xx = 'handle'>
xx:别名
keynum:->可使用event.keyCode 获取对应键位的值

属性绑定

v-bind指令

完全体:
<a v-bind:href= 'url'></a>
简写形式:
<a :href= 'url'></a>

样式绑定

默认的class如何处理呢?->保留

数组可以结合对象一起使用

<div v-bind:class="[activeClass,errorClass,{test:isTest}]"></div>

使用:

<Style>.active {border: 1px solid red;width: 100px;height: 100px;}.error{background-color: yellow;}</Style>
// 对象语法
<div v-bind:class="{active:isActive,error:isError}"></div>
// 数组语法
<div v-bind:class="[activeClass,errorClass]"></div>// 对象简化操作语法
<div v-bind:class="objClasses"></div>
// 数组简化操作语法
<div v-bind:class="arrClasses"></div>
// 内联Style
<div v-bind:style="{border:borderStyle,width:widthStyle,height:heightStyle}"></div>
// 简化内联Style
<div v-bind:style = 'objStyle'></div>data() {return {/*对象语法 开始**/isActive:true,isError:false/*对象语法 结束**/-----------------------------------------------------/*对象简化操作 开始**/objClasses:{active:true,error:true}/*对象简化操作 结束**/-----------------------------------------------------/*数组语法 开始**/activeClass:'active',errorClass:'error'/*数组语法 结束**/-----------------------------------------------------/*数组简化操作 开始**/arrClasses:['active','error']/*数组简化操作 结束**/-----------------------------------------------------/*内联样式 开始**/borderStyle:'1px solid blue',widthStyle:100px,heightStyle:200px/*内联样式 结束**/-----------------------------------------------------/*简化内联样式 开始**/                              objStyle{border:'1px solid blue',width:100px,height:200px}/*简化内联样式 结束**/}

分支结构

  • v-if
  • v-else
  • V-else-if
  • v-show

v-if与v-show的区别

  • V-if 控制元素是否渲染到页面
  • V-show 控制元素是否显示(display:none/’ ')

循环结构

V-for

 <ul><!-- 遍历数组 --><li v-for = 'item in user'>{{item}}</li><!-- 可获取数组下标 --><li v-for = '(item,index) in user'>{{item}}{{index}}</li><!-- 数组对象遍历方式 --><li v-for = 'item in user'>{{item.eName}}------{{item.cName}}</li><!-- key的作用:帮助vue区分不同元素,从而提高性能 --><!-- :key -> bind:key; item.id-> 该对象的id(唯一) --><li :key = 'item.id' v-for = '(item,index) in user'>{{item}}</li></ul>data() {return {//数组fruits:['apple','orange','banana']// 数组对象user:[{eName:'jack',cName:"杰克"},{eName:'john',cName:'路西'},{eName:'john',cName:'约翰'}]}}# item:自定义变量,迭代的元素。

分支循环结构

v-for 遍历对象
<li v-for = '(value,key,index) in obj'></li>
v-for 结合v-if
<li v-if= ’value||key||index‘ v-for = '(value,key,index) in obj'></li>

表单域修饰符

  • number:转换为数值
  • Trim:去掉开始和结尾的空格
  • lazy:将input时间切换为change事件
<input v-model.number = 'num'>
<input v-model.trim = 'info'>
// 输入框失去焦点触发,使用场景:注册用户名,会检测是否已经存在,需要获取该输入框数据
<input v-model.lazy = 'info'>

自定义指令

// 全局指令
Vue.directive('指令名'{inserted:function(el){// 获取元素的焦点el.指令名()}
})// 局部指令
directives:{指令名称:{insered:function(el){el.指令名称}}
}
// 使用方式
<input v-指令名>

计算属性

将复杂的计算逻辑抽取出来,使模板内容更加简洁。

计算属性与方法的区别?

  • 计算属性是基于他们的依赖(data中的数据)进行缓存的(相同的逻辑只执行一次,并将执行的结果缓存)
  • 方法不存在缓存
    <div id="app">{{msg}}{{reverseString}}<input type = 'text' v-model = 'msg'></div>computed: {reverseString:function(){// 有返回值 需要return return this.msg.split('').reverse().join('')}}

侦听器

描述:数据变化会触发侦听器锁绑定的方法

使用场景:数据变化时执行异步或开销较大的操作

    var vm = new Vue({el: '#app',data() {return {firstName:'张',lastName:'桂源',fullName:'张桂源',}},// 侦听器watch: {// 侦听的属性名与data中的属性名一致// val:变化后的数据firstName:function(val){this.fullName = val + ' ' + this.lastName},lastName:function(val){this.fullName = this.firstName + ' ' + val}},})

过滤器

作用:格式化数据,比如讲字符串格式化为首字母大写,将日期转化为指定格式。

    // 创建一个全局的过滤器Vue.filter('filter1',function(val){return val.charAt(0).toUpperCase()+val.slice(1)})// 使用方式{{msg | filter1}}// 可以多个条件{{msg | filter1 | filter2}}// 可以对绑定的数据进行过滤<div v-bind:id = 'id | filter'></div>// 局部filterfilters:{filter1:function(){},filter2:function(){}
}// 时间格式化{{date | dateFormat('yyyy-MM-dd')}}// 第二个形参为传递的参数Vue.filter('dateFormat',function(value,arg){// 时间转换格式})

变异方法(修改原有数据)

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

替换数组(生成新的数组)

filter()、concat()、slice() 不会改变原数组,都返回一个心的数组

vm.list[1] = 'lemon' // 通过这种下表修改属性值,不具备响应式Vue.set(vm.list,index,'lemon') // 具备响应式
vm.$set(vm.list,index,'lenmon')  // 具备响应式// 对象
vm.$set(vm.info,'gender','female');// 具备响应式
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head>
<script type="text/javascript" src="./js/vue.js"></script>
<!-- 导入路由js -->
<script type="text/javascript" src="./js/vue-router.js"></script>
<Style>.active {border: 1px solid red;width: 100px;height: 100px;}.error {background-color: yellow;}
</Style><body><div id="app"><div>编号:<input class="form-control" type="text" v-model='id' :disabled="flag">书名:<input class="form-control" type="text" v-model='name'><span>{{total}}</span><button @click='handle' :disabled='butFlag'>添加</button></div><table><tr :key='item.id' v-for='(item,index) in books'><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.now}}</td><td><a href="" @click.prevent='edit(item.id)'>修改</a><span @click.prevent>|</span><a href="" @click.prevent='del(item.id)'>删除</a></td></tr></table></div></body>
<script>// 第二个形参为传递的参数Vue.filter('dateFormat', function (value, arg) {// 时间转换格式})var vm = new Vue({el: '#app',data: {id: '',name: '',flag: false,butFlag: false,books: []},computed: {total: function () {return this.books.length}},watch: {name: function (val) {var flag = this.books.some(function (item) {return item.name == val;});if (flag) {this.butFlag = true;} else {this.butFlag = false;}}},mounted() {this.books = [{id: 1,name: '围城'},{id: 2,name: '三体'},{id: 3,name: '白夜行'}]},methods: {handle: function () {if (this.flag) {// 修改操作this.books.some((item) => {if (item.id == this.id) {item.name = this.namereturn true;}})this.flag = false} else {// 添加var book = {};book.id = this.id;book.name = this.name;this.books.push(book);}// 将文本清空this.id = '';this.name = '';},edit: function (id) {this.flag = true;var book = this.books.filter(function (item) {return item.id == id;})this.id = book[0].id;this.name = book[0].name;},del: function (id) {this.books = this.books.filter(function (item) {return item.id != id})}},})
</script></html>

组件化开发

  • 命名方式 短横线:xxx-xxx 驼峰式:HelloWorld ,
  • 组件模板必须是唯一的根节点
  • 组件data 必须是一个函数(组件可复用且互不影响)
   // 使用<button-count></button-count><button-count></button-count><button-count></button-count>// 全局组件注册Vue.component('button-count',{// 组件数据 必须是一个函数data:function(){return {count :0}},// 组件模板内容template:`<button @click = "count++">点击了{{count}}次</button>`})// 局部组件使用
可以声明在外部
var button-count = {data:function(){return{msg:"HelloWorld"}},template:`<div><span>{{msg}}</span></div>`
}var vm = new Vue({el:'',data:{},components:{'button-count':button-count}
})

父组件向子组件传值

  • 组件内部通过Props接受传递过来的值

    • props中使用驼峰形式,模板中需要使用短横线的形式
    • 字符串形式的模板中没有这个限制(template中)
    • props传递原则:
    Vue.component('menu-item',{props:['title'],
    temelate:`<div>{{title}}  </div>`
    })
    
  • 父组件通过属性将值传递给子组件

    <menu-item title = "来自父组件的数据"></menu-item>
    <menu-item :title = 'title'></menu-item>
    

子组件向父组件传递信息

  • 子组件通过自定义事件向父组件传递信息
`<button @click = '$emit("enlarge-text")'>扩大父组件字体大小</button>`
  • 父组件监听子组件的事件
<menu-item @enlarge-text = 'handle'></menu-item>

子组件向父组件传值

`<button @click = '$emit("enlarge-text",0.1)'>扩大父组件字体大小</button>`

父组件监听子组件的事件

<menu-item @enlarge-text = 'fontSize += $event'></menu-item>

非父子组件间传值

  • 单独的事件中心管理组件间的通信
var eventHub = new Vue()
  • 监听事件与销毁事件
eventHub.$on('add-todo',addTodo)
eventHub.$off('add-todo') // 一般在vue实例中销毁
  • 触发事件
eventHub.$emit('add-todo',id)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aqhM5kIn-1596001043677)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-27 下午4.52.04.png)]

组件插槽的作用

父组件向子组件传递内容

<slot>content-text</slot>

购物车案例

 var CartTitle = {props: ['uname'],template: `<div class="title">{{uname}}的商品</div>`}var CartList = {props: ['list'],template: `<div><div :key='item.id' v-for='item in list' class="item"><img :src="item.img"/><div class="name">{{item.name}}</div><div class="change"><a href="" @click.prevent='sub(item.id)'>-</a><input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/><a href="" @click.prevent='add(item.id)'>+</a></div><div class="del" @click='del(item.id)'>×</div></div></div>`,methods: {changeNum: function(id, event){this.$emit('change-num', {id: id,type: 'change',num: event.target.value});},sub: function(id){this.$emit('change-num', {id: id,type: 'sub'});},add: function(id){this.$emit('change-num', {id: id,type: 'add'});},del: function(id){// 把id传递给父组件this.$emit('cart-del', id);}}}var CartTotal = {props: ['list'],template: `<div class="total"><span>总价:{{total}}</span><button>结算</button></div>`,computed: {total: function() {// 计算商品的总价var t = 0;this.list.forEach(item => {t += item.price * item.num;});return t;}}}Vue.component('my-cart',{data: function() {return {uname: '张三',list: [{id: 1,name: 'TCL彩电',price: 1000,num: 1,img: 'img/a.jpg'},{id: 2,name: '机顶盒',price: 1000,num: 1,img: 'img/b.jpg'},{id: 3,name: '海尔冰箱',price: 1000,num: 1,img: 'img/c.jpg'},{id: 4,name: '小米手机',price: 1000,num: 1,img: 'img/d.jpg'},{id: 5,name: 'PPTV电视',price: 1000,num: 2,img: 'img/e.jpg'}]}},template: `<div class='cart'><cart-title :uname='uname'></cart-title><cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list><cart-total :list='list'></cart-total></div>`,components: {'cart-title': CartTitle,'cart-list': CartList,'cart-total': CartTotal},methods: {changeNum: function(val) {// 分为三种情况:输入域变更、加号变更、减号变更if(val.type=='change') {// 根据子组件传递过来的数据,跟新list中对应的数据this.list.some(item=>{if(item.id == val.id) {item.num = val.num;// 终止遍历return true;}});}else if(val.type=='sub'){// 减一操作this.list.some(item=>{if(item.id == val.id) {item.num -= 1;// 终止遍历return true;}});}else if(val.type=='add'){// 加一操作this.list.some(item=>{if(item.id == val.id) {item.num += 1;// 终止遍历return true;}});}},delCart: function(id) {// 根据id删除list中对应的数据// 1、找到id所对应数据的索引var index = this.list.findIndex(item=>{return item.id == id;});// 2、根据索引删除对应数据this.list.splice(index, 1);}}});var vm = new Vue({el: '#app',data: {}});

前后端交互

要保证多个异步请求顺序:需要嵌套ajax

Promise

常用APi

实例方法

  • p.then()得到异步任务的正确结果
  • p.catch()得到异常信息
  • p.finally()成功与否都会执行(尚且不是正式标准)

对象方法

  • Promise.all()并发处理多个异步任务,所有任务都执行完成才能得到结果
  • Promise.race()并发处理多个异步任务,只要有一个任务完成就能得到结果,其他任务仍执行,只是不关心其他任务执行的状态。

异步编程的一种解决方案,从语法上讲,Promise是一个对象,它可以获取异步操作消息

使用Promise 主要有一下好处:

  • 可以避免多层异步调用嵌套问题
  • Promise对象提供简洁的API,使得控制异步操作更加容易

Promise基本用法

  • 实例化Promise对象,构造函数中传递函数,该函数中用于处理异步任务
  • resolve和reject 两个参数用于处理成功和失败两种情况,并通过p.then获取处理结果。
var p = new Promise(function(resolve.reject){//成功时调用resolve()//失败时调用reject()
});
p.then(function(ret){// 从resolve 得到正常结果},
function(ret){// 从reject 得到错误信息}
)

实例:

    var promise  = new Promise(function(resovle,reject){var flag = false;if(flag){// 正确的逻辑resovle('ok')}else{// 错误的逻辑reject('fuck!')}});promise.then(function(msg){console.log(msg)},function(msg){console.log(msg)})-----------------------------------------------------------------function queryData(url){var promise = new Promise(resolve,reject){var xhr = new XMLHttpRequest();if(xhr.readyState != 4)return;if(xhr.readyState == 4 && xhr.status == 200){resolve('success');}else{reject('error')}xhr.open('get',url);xhr.send(null)}return p;}
queryData('url')
.then(function(msg){// 正常时},function(msg){// 异常时}
)
// 异常时
.catch(function(msg){})var p1 = queryData(url)
var p2 = queryData(url)
var p3 = queryData(url)// Promise.all()并发处理多个异步任务,所有任务都执行完成才能得到结果
Promise.all([p1,p2,p3]).then(function(result){console.log(result)})
// Promise.race()并发处理多个异步任务,只要有一个任务完成就能得到结果,其他任务仍执行,只是不关心其他任务执行的状态。
Promise.race([p1,p2,p3]).then(function(result){console.log(result)})

then参数中的函数返回值

  • 返回Promise实例对象

    • 返回该实例对象会调用下一个then
  • 返回普通值
    • 返回的普通值会直接传递给下一个then,通过then参数中函数的参数接収该值

接口调用fetch用法

基本特性:

  • 更加简单的数据获取方式,功能更强大,更灵活,可以看做是xhr的升级版
  • 基于Promise实现

响应数据格式:

  • text():将返回体处理成字符串类型
  • json():将返回结果和JSON.parse(responseText)一样

常用配置选项

  • method(string):Htpp的请求方法,[GET/POST/PUT/DELETE]
  • body(string):Http的请求参数
  • headers(Object):Http的请求头,默认为{}

语法结构:

fetch(url).then(fn2).then(fu3).then(fu3)....then(fn)fetch('/abc').then(data=>{return data.text();// fetch 特有的text,这里返回的是一个promise实例对象,用于获取后台的数据
}).hten(ret=>{// 注意! 这里得到的才是最终的数据console.log(ret)
})#配置选项 GET
fetch('/abc?id = 123'|rest风格:'/abc/123',{method:'get'
})
#rest接收
app.get('/abc/:id',(rqs,rps))#配置选项 POST
fetch('/abc?id = 123'|rest风格:'/abc/123',{method:'post',body:'name = list&pwd = 123',headers:{'Content-Type':'application/x-www-form-urlencoded'}
})#配置选项 POST ->JSON
fetch('/abc?id = 123'|rest风格:'/abc/123',{method:'post',body:JSON.stringify({name:'jack',pwd:'456'
}),headers:{'Content-Type':'application/json'}
})

axios

基于Promise用于浏览器和Node.js的Http客户端

特性
  • 支持浏览器和Node.js
  • 支持promise
  • 能拦截请求和响应
  • 自动转换JSON数据
响应结果的主要特性
  • data: 实际响应回来的数据
  • Headers:响应头信息
  • Status:响应状态码
  • statusText:响应状态信息
axios的全局配置

Axios.defaults.timeout = 3000; //设置超时

axios.defaults.baseURL = //默认地址

axios.defaults.headers[‘mytoken’] = ‘11asdasdklh’ // 设置请求头

axios.get('/adata').then(ret=>{// data属性名称是固定的,用于获取后台响应的数据console.log(ret.data)
})//配置请求头信息,对于跨域来讲,此操作需要配置该请求头信息。
axios.defaults.headers['mytoken'] = 'myToken';// 配置请求的基准URL地址
axios.defaults.baseURL = ’http://localhost:8090‘;// 会自动拼接该baseUrl
axios.get('axios-json').then(function(ret){console.log(ret.data)
})
常用API
1.GET传递参数
  • 通过URL传递参数
  • 通过params选项传递参数
#通过url传参
axios.get('/adate?id=123').then(ret=>{console.log(ret.data)
})
#通过params传参
axios.get('/adate',{//对象params:{id:789}
}).then(function(ret){console.log(ret.data)
})#响应请求
app.get('/adata',(req.res)=>{res.send(req.query.id)
}
#result请求
app.get('adata/:id',(req,res)=>res.send(req.params.id)
)
2.DELETE传递参数
#通过url传参
axios.delete('/adate?id=123').then(ret=>{console.log(ret.data)
})
#params传参
axios.delete('/adate',{//对象params:{id:789}
}).then(function(ret){console.log(ret.data)
})#响应请求
app.delete('/adata',(req.res)=>{res.send(req.query.id)
}
3.POST参数传递

通过选项传递参数(默认传递的是json格式数据)

axios.post('/adata',{uname:'tom',pwd:123
}).then(ret=>{console.log(ret.data)
})#响应请求
app.post('/adata',(req.res)=>{res.send(req.body.uname+'-----'req.body.pwd)
}

表单形式参数

通过URLSearchParams传递参数(application/x-www.form-urlencoded)

// 创建实例
const params = new URLSearchParams();
// 添加参数
params.append('key1',value1);
params.append('key2',value2);
// 将params放入请求中并发送请求
axios.post('/api/test',params).then(ret=>{console.log(ret.data)
})
4.PUT请求传参
// JSON形式
axios.put('/adata',{uname:'tom',pwd:123
}).then(ret=>{console.log(ret.data)
})#响应请求
app.put('/adata',(req.res)=>{res.send(req.body.uname+'-----'req.body.pwd)
}// 表单形式->创建实例
const params = new URLSearchParams();
// 添加参数
params.append('key1',value1);
params.append('key2',value2);
// 将params放入请求中并发送请求
axios.put('/api/test',params).then(ret=>{console.log(ret.data)
})
axios拦截器
请求拦截器

axios->请求拦截器->客户端

axios.interceptors.request.use(function(config){console.log(config.url)config.headers.mytoken = 'myToken';// 需要return出去,不然拦截器配置失效return config},function(err){console.log(err)})axios.get('url').then(function(data){console.log(data)
})
响应拦截器

在获取数据之前对数据做一些加工处理

服务器->响应拦截器->axios

axios.interceptors.response.use(function(res){console.log(res)// 这里直接拿到了data数据var data = res.data;// 返回给axiosreturn data},function(err){console.log(err)})axios.get('url').then(function(data){// 打印出来的是后台传递的数据console.log(data)
})

async

  • asunc/await 是ES7引入的新语,可以更加方便的进行异步操作
  • async 关键字用于函数上(async函数的返回值是Promise实例对象)
  • await 关键字用于async函数当中(await可以得到异步的结果)
// 返回值promise对象
async function querData(id){const ret = await axios.get('/data');return ret;
}
querData().then(function(data){console.log(data)
})

aysnc/await处理多个异步请求

aysnc function queryData(){var info = await axios.get('async1');var ret = await axios.get('async2?info'+info.data)return ret.data
}
quertData().then(function(data){console.log(data)
})

Router路由

1.后端路由

  • 概念:根据不同的用户URL请求,返回不同的内容
  • 本质:URL 请求地址与服务器资源之间的对应关系

2.前端路由

  • 概念:根据不同的用户事件,显示不同的页面内容
  • 本质:用户事件与事件处理函数之间的对对那个欢喜

Vue Router 包含功能

  • 支持HTML5历史模式或hash模式
  • 支持嵌套路由
  • 支持路由参数
  • 支持编程式路由
  • 支持命名理由

使用步骤:

// router-link是 vue 中提供的标签,默认会被渲染为a标签
// to 属性默认会被渲染为 href 属性
// to 属性的值会被默认渲染为 # 开头的 hash地址
// 1. 创建路由
<router-link to = '/register'>Register</router-link>
<router-link to = '/user'>User</router-link>// 2.添加路由占位符
<router-view></router-view>// 3.定义路由组件
const User = {template:'<h1>User组件</h1>'
}
const Register = {template:'<h1>Register</h1>'
}
// 4. 配置路由规则并创建路由实例var router = new VueRouter({// routes 是路由规则数组routes:[// 每个路由规则都是一个配置对象,其中至少包含path 和component两个属性// path:表示当前路由规则匹配的 hash 地址// component:表示当前路由规则对应要展示的组件{path:'/user',component:User},{path:'/register',conponent:Register}]})// 5.把路由挂载到Vue根实例中const vm = new Vue({el:'#app',data:{},// 挂载路由实例对象// 在ES6 中属性名跟变量相同可以直接写上变量值// router:routerrouterrouter})

路由重定向

用户在访问地址 A 的时候,强制用户跳转到 地址 c,从而展示特定的组件页面。

通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便的设置路由的重定向

var router = new VueRouter({routes:[// 其中,path 表示需要被重定向的 原地址,redirect 表示被重定向到哪里{path:'/',redirect:'/user'},{path:'/user',component:User},{path:'/register',conponent:Register}]
})

路由嵌套

  • 点击父级路由链接显示模板内容
  • 模板内容中又有子级路由链接
  • 点击子级路由链接显示子级模板内容[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XFEXc8k8-1596001043681)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-28 上午10.55.46.png)]
    const Register = {template:`<div><h1>register</h1>// 子组件路由<router-link to = "/register/tab1">tab1</router-link><router-link to = "/register/tab2">tab2</router-link>// 子组件路由占位符<router-view></router-view></div>`}const  router = new VueRouter({routes:[{path:'/',redirect:'/user'},{path:'/user',component:User},// 构建关系{path:'/register',component:Register,// children[]子规则children:[{path:'/register/tab1',component:Tab1},{path:'/register/tab2',component:Tab2}]},]})

路由动态匹配

基本用法

$route与对应路由形成高度耦合,不够灵活,所以可以使用props将组件和路由解耦

<router-link to="/user/1">User1</router-link>
<router-link to="/user/2">User2</router-link>
<router-link to="/user/3">User3</router-link>var router = new VueRouter({routes:[// 动态路径参数,以冒号开头{path:'/user/:id',component:User}]
})const User = {template:'<div>// 路由组件中通过$route.params获取路由参数User{{$route.params.id}}</div>'
}

props的值为布尔类型

const router = new VueRouter({routes:[//如果 props 被设置为 true ,route.params 将会被设置为组件属性{path:'/user/:id',component:User,props:true}]
})
const User = {props:['id'], //使用props接受路由参数template:'// 使用路由参数<div>{{id}}</div>'
}# 接受一个对象时
const router = new VueRouter({routes:[//如果 props 被设置为 true ,route.params 将会被设置为组件属性{path:'/user/:id',component:User,props:{uname:'jack',age:23}}]
})
const User = {// 此时再想去接受id 已经不能访问了,因为props 已经是一个对象了,Props中有什么,才能取什么!props:['uname','age'], //使用props接受路由参数template:'// 使用路由参数<div>{{id}}</div>'
}# 接受一个对象,并拿到id
const router = new VueRouter({routes:[//如果 props 被设置为 true ,route.params 将会被设置为组件属性{path:'/user/:id',component:User,// roite->动态参数对象 props:route => ({uname:'jack',age:20,id:route.params.id})}}]
})
const User = {props:['uname','age','id'], //使用props接受路由参数template:'// 使用路由参数<div>{{id+'-'+uname+'-'+age}}</div>'
}

路由命名

为了更加方便的表示路由的路径,可以给路径规则起个别名

// name:路由别名 params:需要的参数
<router-link :to="{name :'user',params:{id:3}}"></router-link>const router = new VueRouter({routes:[{// 该路由别名name:'user',path:'/user/:id',component:User}]
})

页面导航的两种方式

  • 声明式导航:通过点击链接实现导航的方式

    • 链接 或vue中的
  • 编程式导航:通过调用JavaScript形式的API实现导航的方式,叫做编程式导航
    • 例如:普通网页中的location.href
编程式导航基本用法
  • this.$router.push(‘hash地址’)
  • this.$router.go(n) // 历史记录的前进或后退
    // 定义路由组件const User = {template:`<div><button @click = 'goRegister'>去注册</button></div>`,methods: {goRegister:function(){// 完成导航功能this.$router.push('/register')}},}const Register = {template:`<div><h1>register</h1><button @click = 'goBack'>回到上一页</button></div>`,methods: {goBack:function(){this.$router.go(-1)}},}
后台管理实例
  <script>// 定义 APP 根组件const App = {template: `<div><!-- 头部区域 --><header class="header">传智后台管理系统</header><!-- 中间主体区域 --><div class="main"><!-- 左侧菜单栏 --><div class="content left"><ul><li><router-link to="/users">用户管理</router-link></li><li><router-link to="/rights">权限管理</router-link></li><li><router-link to="/goods">商品管理</router-link></li><li><router-link to="/orders">订单管理</router-link></li><li><router-link to="/settings">系统设置</router-link></li></ul></div><!-- 右侧内容区域 --><div class="content right"><div class="main-content"><router-view /></div></div></div><!-- 尾部区域 --><footer class="footer">版权信息</footer></div>`}const Users = {data() {return {userlist: [{ id: 1, name: '张三', age: 10 },{ id: 2, name: '李四', age: 20 },{ id: 3, name: '王五', age: 30 },{ id: 4, name: '赵六', age: 40 }]}},methods: {goDetail(id) {console.log(id)this.$router.push('/userinfo/' + id)}},template: `<div><h3>用户管理区域</h3><table><thead><tr><th>编号</th><th>姓名</th><th>年龄</th><th>操作</th></tr></thead><tbody><tr v-for="item in userlist" :key="item.id"><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.age}}</td><td><a href="javascript:;" @click="goDetail(item.id)">详情</a></td></tr></tbody></table></div>`}const UserInfo = {props: ['id'],template: `<div><h5>用户详情页 --- 用户Id为:{{id}}</h5><button @click="goback()">后退</button></div>`,methods: {goback() {// 实现后退功能this.$router.go(-1)}}}const Rights = {template: `<div><h3>权限管理区域</h3></div>`}const Goods = {template: `<div><h3>商品管理区域</h3></div>`}const Orders = {template: `<div><h3>订单管理区域</h3></div>`}const Settings = {template: `<div><h3>系统设置区域</h3></div>`}// 创建路由对象const router = new VueRouter({routes: [{path: '/',component: App,redirect: '/users',children: [{ path: '/users', component: Users },{ path: '/userinfo/:id', component: UserInfo, props: true },{ path: '/rights', component: Rights },{ path: '/goods', component: Goods },{ path: '/orders', component: Orders },{ path: '/settings', component: Settings }]}]})const vm = new Vue({el: '#app',router})</script>

前端工程化

模块化相关规范
  • 模块化,就是把单独的一个功能封装到一个模块(文件中),模块之间相互隔离,但是可以通过特定的接口公开内部成员,也可以依赖别的模块
  • 模块化开发的好处:方便代码复用,提高开发效率,便于维护

服务器端模块化规范

CommonJS

  1. 模块分为 单文件模块 与 包
  2. 模块成员导出: module.exports 和exports
  3. 模块成员导入:require(‘模块标识符’)

ES6模块化规范

ES6模块化规范,是浏览器端与服务器端通用的模块化开发规范。

  1. 每个 js 文件都是一个独立的模块
  2. 导入模块成员使用 import 关键字
  3. 暴露模块成员使用 export 关键字

默认导出

  • 默认导出语法 export default 默认导出的成员
  • 在每个模块中只能使用一次export default 否则将会报错
// 当前文件模块为 m1.js
let a = 10;
let c = 20;
let d = 30
function show();
// 将本模块中的私有成员暴露出去,供其他模块使用
export default{a,c,show
}

默认导入

  • 默认导入语法 import 接受名称 from ‘模块标识符’
// 导入模块成员
import m1 form './m1.js'
console.log(m1)
// 打印输出结果为:
{a:10,c:20,show:[function:show]}

按需导出

  • 按需导出语法 export let s1 = 10
// 按需导出
export let r = 10;
export let g = 20;
export function say(){console.log(sayFunction)
};

按需导入

  • 按需导入语法 import {s1} from ‘模块标识符’
// 如果想按需导出跟默认导出同时出现 需要使用',{}' 可以使用as 给该属性起别名
import m1,{r,g as gg,say}from './m1.js'// console.log(m1)
console.log(say)

直接导入并执行模块代码

有时候,我们只想单纯执行某个模块中的代码,并不需要得到模块中向外暴露的成员,

此时,可以直接导入并执行模块代码。

// m2模板中的内容
for(let i = 0;i<3;i++){console.log(i)
}
// index.js 中导入
import './m2.js'//控制台输出结果123

webpack

概述:

一个流行的前端项目构建工具(打包工具),webpack提供了友好的模块化支持,以及代码压缩混淆处理js兼容问题性能优化等强大功能,提高开发效率和项目的可维护性。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-506vQ6LR-1596001043683)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-28 下午8.58.14.png)]

使用

->创建列表隔行变色项目

  1. 新建项目空白目录,并运行 npm init -y 命令,初始化包管理配置文件 package.json
  2. 新建 src 源代码目录
  3. 新建 src -> index.html 首页
  4. 初始化首页基本的结构
  5. 运行 npm install jquery -s 命令,安装jQuery
  6. 通过模块化的形式,实现列表隔行变色效果

此处有ES6兼容性问题,需要配合webpack使用。

  • 在项目根目录中,创建名为webpack.config.js 的webpack配置文件

1.webpack配置文件:

module.exports = {mode:'development' // mode 用来指定构建模式,development该值为开发模式,代码不会混淆、压缩
}

2.在package.json 配置文件中的scripts节点下,新增dev 脚本如下

"scripts":{"dev":"webpack" // script 节点下的脚本,可以通过npm run执行
}

3.在终端中运行 npm run dev 命令,启动 webpack 进行项目打包。

配置打包的入口与出口
  • 打包的入口文件为 scr -> index.js
  • 打包的输出文件问 dist -> main.js

如果需要修改打包的入口与出口,可以在 webpack.config.js 中新增如下配置信息:

const path = require('path') // 导入 node.js中专门操作路径模块
module.exports = {entry:path.join(_dirname,'./src/index.js'), // 打包入口文件位置output:{path:path.join(_dirname,'./dist'),// 输出文件的存放路径filename:'bundle.js' // 输出文件的名称}
}

配置自动webpack打包功能

  • 运行 npm install webpack-dev-server -D命令,安装支持项目自动打包的工具
  • 修改 package.json ->scripts 节点中的dev命令如下
"scripts":{"dev":"webpack-dev-server" // script 节点下的脚本,可以通过 npm run dev执行
}
  • 将 src -> index.html 中,script 脚本的引用路径,修改为’bundle.js’
  • 运行 npm run dev 命令重新打包
  • 访问 http://localhost:8080 查看自动打包效果
  • 在文件夹中生成的bundle.js 文件运行在内存中
    • 注意:

      1. webpack-dev-server 会启动一个实时打包的 http 服务器
      2. webpack-dev-server 打包生成的输出文件,默认放到项目根目录中,并隐藏。
配置生成预览页面
  • npm install html-webpack-plugin -D 下载插件
  • 修改 webpack.config.js 文件头部区域,添加如下配置:
// 导入生成预览页面的插件,得到一个 构造函数
const HtmlWebpackPlugin = require('html-web-plugin');
const htmlPlugin = new HtmlWebpackPlugin({template:'./src/index.html',//指定要用到的模板文件filename:'index.html' // 指定生成的文件的名称,该文件存在于内存中,在项目目录中不现实
})
  • 修改webpack.config.js文件中对外暴露的配置对象,新增如下配置节点
module.exports = {plugins:[htmlPlugin]// plugins 数组是 webpack 打包期间会用到的一些插件列表
}
打包完成自动打开项目

配置参数

//package.json 中配置
// --open 打包完成自动打开浏览器页面
// --host 配置 Ip 地址
// --port 配置端口
"scripts":{"dev":"webpack-dev-server --open --host 127.0.0.1 --port 8088"
}
通过loader 打包非js模块

在实际开过程中,webpack 默认只能打包处理以 .js 后缀名的模块,其他非 .js 后缀名结尾的模块,webpack默认处理不了,需要调用 loader 加载器才可以正常打包,否则会报错。

loader 加载器可以协助 webpack 打包处理特定的文件模块,比如:

  • less-loader 可以打包处理 .less 相关的文件
  • sass-loader 可以打包处理 .scss 相关的文件
  • url-loader 可以打包处理 css 中与 url路径相关的文件[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uDNB1R4C-1596001043684)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-29 上午10.41.54.png)]
打包处理css文件
  1. npm i style-loader css-loader -D 安装处理css文件的 loader
  2. 在 webpack.config.js 的 module ->rules 数组中,添加 loader规则如下
# webpack.config.js
// 所有第三方文件模块的匹配规则module:{rules:[{test:/\.css$/,use:['style-loader','css-loader']}]}
# test 表示匹配的文件配型, true 表示对应要调用的loader
# use 数组中指定的 loader 顺序是固定的
# 多个 loader 的调用顺序是:从后往前调用
打包处理less 文件
  1. npm i less-loader less -D 安装处理 less 文件的 loader
  2. 在 webpack.config.js 的 module ->rules 数组中,添加 loader规则如下
# webpack.config.js
// 所有第三方文件模块的匹配规则module:{rules:[{test:/\.less/,use:['style-loader','css-loader','less-loader']}]}
打包处理scss 文件
  1. npm i sass-loader node-sass -D 安装处理 sass 文件的 loader
  2. 在 webpack.config.js 的 module ->rules 数组中,添加 loader规则如下
# webpack.config.js
// 所有第三方文件模块的匹配规则module:{rules:[{test:/\.scss/,use:['style-loader','css-loader','sass-loader']}]}
配置postCSS 自动添加css的兼容前缀
  1. 运行 npm i postcss-loader autoprefixer -D命令
  2. 在项目根目录中创建 postcss 的配置文件 postcss.config.js并初始化如下配置
const autoprefixer = require('autoprefixer') // 导入自动添加前缀插件
module.exports = {plugins:[autoprefixer] //挂载插件
}

3.在 webpack.config.js 的 module->rules 数组中,修改 css 的loader规则如下

module:{rules:[{test:/\.css$/,use:['style-loader','css-loader','postcss-loader']}]
}

打包处理图片 文件字体文件

  1. npm i url-loader file-loader -D
  2. 在 webpack.config.js 的 module ->rules 数组中,添加 loader规则如下
module:{rules:[{test:/\.jpg|png|git|bmp|ttf|eot|svg|woff|woff2$/,use:'url-loader?limit=16940' // 图片大小 单位字节(byte)}]
}
打包处理js文件的高级语法
  1. 安装babel 转换器相关的包:npm i babel-loader @babel/core @babel/runtime -D
  2. 安装babel 语法插件相关的包:npm i @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D
  3. 在项目跟目录中,创建babel配置文件babel.config.js 并初始化如下配置:
module.exports = {presets:['@babel/preset-env'],plugins:['@babel/plugin-transform-runtime','@babel/plugin-proposal-class-properties'] //挂载插件
}

4.在 webpack.config.js 的module -> rules 数组中,添加 loader 规则如下:

// exclude 为排除项 表示babel-loader 不需要处理:node_modules中的js文件
{test:/\.js$/,use:'babel-loader',exclude:/node_modules/}

Vue 单文件组件

传统组件的问题和解决方案
  1. 全局定义的组件必须保证组件的名称不重复
  2. 字符串模板缺乏语法高亮
  3. 不支持CSS意味着当HTML和JavaScript 组件化时,CSS明显会被遗漏。
  4. 没有构建步骤限制,只能使用HTML和ES5 JavaScript,而不能使用预处理器(如:babel)

vue进阶之路 webpack打包 持续更新相关推荐

  1. 软件测试进阶之路 - 目录 (持续更新)

    1. 软件测试基础 1-1 计算机基础 1-1.1 近代计算机的发展史 1-1.2 计算机分代 1-1.3 计算机分类 1-1.4 计算机的组成 1-1.5 操作系统基础 1-1.6 Windows ...

  2. 【我的OpenGL学习进阶之旅】【持续更新】关于学习OpenGL的一些资料

    目录 一.相关书籍 OpenGL 方面 C方面 NDK 线性代数 二.相关博客 2.0 一些比较官方的链接 2.1 OpenGL着色器语言相关 2.2 [[yfan]](https://segment ...

  3. 一幅长文细学Vue(一)——Webpack打包工具

    1 项目开发工具 摘要 ​ 在本文中,我们会详细讨论webpack是如何打包发布项目,不过对于Vue来说,Vite可以做到和webpack一样的功能. 声明:如果想要看懂此文章,需具备node.js中 ...

  4. webpack文章(持续更新)

    webpack文章: http://webpackdoc.com/install.html webpack打包:http://webpackdoc.com/ (先把文章看一篇,然后照着模仿一遍) ht ...

  5. 学习VUE时,利用webpack打包的错误处理方法

    今天看哔哩哔哩Vue的课程时候遇到三个问题记录一下 1.安装webpack npm install webpack -g 新版需要同时安装 2.安装webpack-cli npm install -- ...

  6. 前端(以Vue为例)webpack打包后dist文件包如何部署到django后台中

    由于现在前端使用的三大框架配合webpack可以实现快速打包,为部署到服务端提供了非常大的便利,那么在前端打包后,应该做些什么可以部署到django的后台中呢? 1.打包后文件包dist 进入到 di ...

  7. 前端自学Vue笔记干货(第一版,持续更新中~~~)

    学习笔记 Vue笔记 nprogress使用 npm i nprogress -S 基本上都是在对axios进行二次封装.前置守卫路由或者封装成工具函数的.js文件中用到 import nprogre ...

  8. C++_Leetcode刷题之路——简单(持续更新)

    目录 1. 两数之和 解一: 解二: 7. 整数反转 解一: 9. 回文数 解一: 13. 罗马数字转整数 解一: 14. 最长公共前缀 解一: 20. 有效的括号 解一: 21. 合并两个有序链表 ...

  9. vue+elementUI项目的踩坑~~持续更新

    日常工作记录,如果对你有帮助请点亮小~心~心喔~~~ 接受礼貌的批评指导 目录: 1.el-form只有一个搜索条件时页面刷新问题: 2.el-tree树形控件,给项目安装jsx语法: 3.el-ca ...

最新文章

  1. linux mint 相关环境配置
  2. python3 读取.plist文件_Python学习笔记 -5 - 文件操作
  3. [trustzone]-ARM trustzone技术下常见的软件框图
  4. android classloader的功能和工作模式,Android中ClassLoader和java中ClassLoader有什么关系和不同...
  5. 组合数据类型练习、英语词频统计
  6. mysql连接不上怎么重置密码错误_MySQL数据库连接不上、密码修改问题
  7. 前端图片有时候能显示有时候不显示_web前端基础教程:两种数据存储思路
  8. Python使用装饰器捕获异常
  9. Leetcode 64. 最小路径和 -- DP算法
  10. 查找算法-------插值查找
  11. python判断字符类型例题_Python面试题:字符类型的考察
  12. 软考中级软件设计师该怎么备考
  13. MMDetection CenterNet 源码解析
  14. QWebEngineView崩溃及替代方案
  15. jquery的图片播放插件 - colorbox
  16. 晕LIVEnbsp;WRITER设置教程是网易…
  17. 脑机接口技术使瘫痪病人重获运动能力,4D打印技术已经到来!|技术前沿洞察...
  18. TC358775XBG是一颗将MIPI DSI信号转换成single/ dual -link LVDS的芯片,最高分辨率支持到1920x1200
  19. “人工智能”•从入门到入土 –导言
  20. 因果6-估计因果效应

热门文章

  1. 微信小程序开发环境搭建
  2. 一篇文入门微信小程序开发环境搭建
  3. App Store和苹果tf签名两种App下载方式区别在哪?
  4. 解决 SVN提交代码中太多其他无关文件的问题
  5. Word2Vec原理概述
  6. mysql数据库事务的回滚操作
  7. Smark.Data 功能强大又灵活的Expression
  8. php日期和时间的应用
  9. Torque配置(转载,感谢羊神)
  10. win10:我们无法创建新的分区,也找不到现有的分区,