Eastboat


  • 首页

  • 归档

  • 我的有料

vue项目开发(二)

发表于 2019-02-20

六 Vue指令(Directive)

条件渲染指令

  • v-if
  • v-else (必须紧跟v-if其后)
  • v-show
    1
    2
    3
    区别:
    1.v-show通过改变元素的css属性display,v-if通过适当的销毁和重建元素,是真正的条件渲染
    2.v-if适合条件不经常改变的场景,v-show适合频繁切换

列表渲染指令v-for

  • 遍历数组

    1
    2
    3
    4
    <div v-for="(item,index) in items"></div>
    // item为每次遍历的元素
    // index为下标索引
    // item表示数组或对象
  • 遍历对象

    1
    2
    3
    4
    5
    <div v-for="(value,key,index) in personObj" :key="index"></div>

    // index 索引
    // key
    // value
  • key属性

    1
    2
    为了方便Vue实例跟踪每个节点的身份,从而重用或重新排列现有的元素
    key需要通过v-bind来绑定动态值,尽可能在使用v-for时提供key属性

方法和事件

v-on:事件名=”fn()” 函数定义在vue的选项methods中,没有参数时候后面的参数可以省略

  • v-on可以简写成@符号
  • 事件修饰符
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
//event的事件可以使用vue事件的修饰符来实现.在@绑定的事件后面加上小圆点,在跟一个后缀来是使用修饰符
1.stop
2.prevent
3.capture
4.self
5.once

<!-- 组织单击事件冒泡-->
<a href="" @click.stop='handle'>阻止单击事件冒泡</a>

<!-- 提交事件不再重载页面 -->
<form action="" @submit.prevent=''handle></form>

<!-- 修饰符可以串联 -->
<a href="" @click.stop.prevent='handle'></a>

<!-- 只有修饰符 -->
<form action="" @submit.prevent></form>

<!-- 添加事件侦听器时使用时间捕获模式 -->
<div @click.capture='handle'>..</div>

<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div @click.self='handle'>..</div>

<!-- 只触发一次,组件同样适用 -->
<div @click.once='handle'>..</div>

<!-- 在表单元素上监听键盘事件时。还可以使用按键修饰符,比如按下具体的某个键时才调用方法 -->
<!-- 只有在keyCode是13时调用vm.submit() -->
<input type="text" @keyup.13='submit'>
<!--
也可以自己配置具体的按键
Vue.config.keyCodes.f1=112;
全局定义后,就可以使用@keyup.f1

除了某个具体的keyCode外,vue还提供了一些快捷名称,以下是全部的别名
.enter
.tab
.delete
.esc
.space
.up
.down
.left
.right
这些按键修饰符还可以组合使用,或和鼠标一起配合使用
.ctrl
.alt
.shift
.meta (mac下Command键 WIndows是窗口键)
-->

表单双向绑定v-model

  • v-model只能用在input,select,textarea等表单元素上
  • 修饰符
    1
    2
    1. lazy将改变默认的同步状态,在change事件中同步
    2. trim将自动过滤输入的首尾空格

七 组件详解

组件概念和复用

使用props传递数据

1
2
3
4
5
/*
组件的内容不仅仅是对模板的复用,更重要的是组件之间的通讯.
父组件向子组件传递数据和参数,子组件接收到参数后再根据参数的
不同来渲染不同的内容或执行操作,这个正向传递的过程就是通过props来实现的
*/

props来声明需要从父组件接受的数据,值类型为数组和对象,对象中可以自定义数据类型

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 class="page">
<h3>这是列表页</h3>
<button @click="skip()">跳转到详情页</button>
<!--使用组件-->
<list-item message="来自列表页的数据"></list-item>
</div>
</template>

<script>
//引入组件
import ListItem from "@/components/ticketlist/listitem"
export defalt{
components:{
ListItem //注册组件
}
}
</script>


//子组件
<template>
<div class="list">
<p>{{message}}</p>
</div>
</template>
<script>
export default{
props:['message']
}
</script>

单向数据流

为了保证父组件和子组件完全解耦,避免子组件无意修改了父组件的状态

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
/*
业务中经常遇到两种需要改变props传递过来数据的情况
*/

//1.父组件传递初始值,子组件将其作为初始值保存起来
<template>
<div class="page">
<!--使用组件-->
<list-item :message="normalVariable"></list-item>
</div>
</template>

<script>
//引入组件
import ListItem from "@/components/ticketlist/listitem"
export defalt{
data(){
return{
mormalVariable:"初始值"
}
},
components:{
ListItem //注册组件
}
}
</script>

//子组件
<template>
<div class="page-child">
<p>{{receive}}</p>
</div>
</templte>
export dafault{
data(){
return{
receive:this.message //只需要维护receive不需要操作message
}
},
props:["message"]
}

//2.另一种情况是props作为需要被转变的原始值传入,这种情况用计算属性就可以了

组件通信

  • 自定义事件以及$emit方法
    1
    2
    3
    4
    5
    6
    this.$emit('event',val)
    /*
    vue实例方法$emit
    参数event为自定义事件名
    参数val代表通过自定义事件传递的值(可选)
    */

子组件传值给父组件的时候,事件触发及接受原则是: 谁触发的监听谁接受

  • 兄弟组件通信的处理方式
    1
    利用props和$emit的综合使用

slot插槽分发内容

  • 匿名插槽

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //假设现在有一个弹出提示框,头部,中间,底部,只有中间区域改变
    // 子组件
    <div class="child>
    <slot>如果没有分内容则显示此段话</slot>
    </div>


    //父组件
    <child>
    <h1>显示弹出的内容</h1>
    </child>
  • 具名插槽

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 子组件
    <div class="child>
    <slot name="CPU">CPU插槽</slot>
    <slot name="GPU">显卡插槽</slot>
    <slot name="Memory">内存插槽</slot>
    <slot name="Hard-drive">硬盘插槽</slot>
    </div>


    //父组件
    <child>
    <div slot="CPU"></div>
    <div slot="GPU"></div>
    <div slot="Memory"></div>
    <div slot="Hard-drive"></div>
    </child>

八计算属性和侦听器

计算属性
1
2
3
1、计算属性是基于他依赖缓存的,一个计算属性所依赖的数据发生变化的时候它才会重新计算取值
2、methods不同,只要重新渲染,方法就会被调,函数就会执行
3、究竟使用什么取决于是否需要缓存,当遍历大数组和计算量很大时,应使用计算属性,除非不希望得到缓存

九插件的使用

状态管理和vuex

  • 安装 cnpm installl vuex –save
  • 使用

    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
    //@/store/index.js
    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)

    export default new Vuex.Store({
    state:{
    //state存放数据
    count:0
    },
    mutations:{
    //状态的变化
    increment:state=>state.count++,
    decrement:state=>state.count--
    }
    })

    // main.js中映入store

    import store from "@/store"
    //在实例中挂载
    new Vue({
    el:"#app",
    store,
    ....
    ...
    ..
    })


    // 新建组件page
    <template>
    <div class="page">
    <Button type="success" @click="increment">+</Button>
    <Button type="success" @click="decrement">-</Button>
    </div>
    </template>

    <script>
    export default {
    data(){
    return{

    }
    },
    methods:{
    increment(){
    this.$store.commit("increment")
    },
    decrement(){
    this.$store.commit("decrement")
    }
    },
    computed:{
    count(){
    return this.$store.state.count
    }
    }
    }
    </script>
  • 总结:引入Vuex后统一的对数据进行管理存放,在各个页面组件中使用commit方法提交mutation对共享数据进行修改

    案例:项目中统一管理登录状态的数据,如购票业务中,未登录的情况下如果点击购票肯定是会提示用户先登录,整个页面都会共享登录信息,也就是token标识,通过vuex很容易就拿到token值,方便后面的操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    const state={
    sLoginInfo:loginInfo //登录信息
    }
    const mutations={
    //登录
    mLogin:(state,loginInfo)=>{
    loginInfo.extTime-=0
    loginInfo.genTime-=0
    state.sLoginInfo=loginInfo
    window.localStorage.setItem(storageKey,JSON.stringify(loginInfo))
    }
    //退出
    mLogout:(state)=>{
    state.sLoginInfo={}
    window.localStorage.removeItem(storageKey)
    }
    }

    export default new Vuex.Store({
    state,mutations
    })

二维码插件 QRCode

微信支付流程

  • 1.确定微信支付时把当前订单号发送给后台
  • 2.后台利用订单号去数据库查询订单信息并整合微信官方等信息后发送给微信官方,微信官方会生成一个携带支付必要信息的链接给后台开发人员
  • 3.后台将微信官方提供的字符串链接返回给前端,利用二维码插件可以将链接生成二维码展示处理,用户扫码就能支付了
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
// cnpm install qrcode --save

<template>
<div class="page">
<div id="canvasArea">
<canvas id="canvas"></canvas>
</div>
</div>
</template>

<script>

import Vue from 'vue'
import QRCode from "qrcode"
Vue.use(QRCode)

export default {
data(){
return{}
},
methods:{
useqrcode(){
//放二维码的容器
var canvas=document.getElementById("canvas")
//调用函数生成二维码,参数依次为 容器,生成的内容,回调函数
QRCode.toCanvas(canvas,"http://www.baidu.com",function(err){
if(err){
console.error(err)
}else{
//成功之后可回调的函数
alert("success")
}
})
}
},
mounted () {
this.useqrcode() //组件挂载的时候调用二维码生成函数
}
}
</script>

<style>

</style>

vue项目开发(一)

发表于 2019-02-15

ES6

let和const命令

  • let

    块级作用域

    变量作用域不会提前声明

    相同作用域内不能重复声明同一个变量
    for循环中的父子作用域

  • const(内存地址不变,所以是常量)

    与let的作用域相同

    const一单声明常量就立即被初始化。

    只声明不赋值也会报错

注意
const一般用在定义模块中的常量
let限制了变量的作用域,保证变量不去影响全局变量,所以尽量用let代替var

变量的解构赋值

数组的解构赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//传统赋值
let a=1;
let b=2;
let c=3;

//es6中解构赋值
let [a,b,c]=[1,2,3]

//允许使用默认值

let [foo=true]=[];
let [boo=1]=[undefined]; // 1
console.log(foo) // true

let [x,y='123']=['a']
console.log(x)// a
console.log(y) // 123

这种写法属于模式匹配,只要等号左右的模式相同,就会对号入座

对象的解构赋值

数组的元素是按次序排列的,变量的取值由特德位置决定的,而对象的属性没有次序,变量必须与属性同名,才能取到正确的值

1
2
3
let {bar,foo}={foo:"123",bar:"222222"};
console.log(bar) //123
console.log(foo) //222222

如果变量名和属性名不一致就应该这样书写(类似重命名属性)

1
2
3
4
5
6
7
8
9
10
11
12
let obj = {less01:"00001",less02:"00002"};
console.log(obj) //{ less01: '00001', less02: '00002' }

let {less01:l1,less02:l2}=obj
console.log(l1) //00001
console.log(l2) //00002

//简写如下形式
let {rightNumber:R,leftNumber:L}=
{rightNumber:"r00001",leftNumber:"l00001"};
console.log(R)
console.log(L)

经验:对象的解构赋值的内部机制,是先找到同名属性 再赋给对应的变量 真正被赋值的是后者,而不是前者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 示例:
let {foo:baz}={foo:"aaa",bar:"bbb"};
console.log(baz); //aaa
console.log(foo) //foo is not defined

foo是匹配的模式
baz才是变量,真正被赋值的是变量baz,而不是模式foo


//对象的结构也可以使用默认值
let {x:y=1234}={};
console.log(y) //1234

let {x:y=1234}={x:5678};
console.log(y) //5678

上机训练

箭头函数

箭头函数起因
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
//setInterval函数是在全局作用域window下执行的,window上找不到属性username和age
const Person={
username:"小陈",
age:"23",
sayHello:function(){
setInterval(function(){
console.log('这是'+this.username+"他今年"+this.age+"岁");
},1000)
}
}

Person.sayHello() //这是undefined他今年undefined岁

// 缓存this进行使用(问题:明明写在对象里面的方法,为什么还要使用缓存对象才能使用呢?)
const Person={
username:"小陈",
age:"23",
sayHello:function(){
let that=this;
setInterval(function(){
console.log('这是'+that.username+"他今年"+that.age+"岁");
},1000)
}
}
Person.sayHello() //这是小陈他今年23岁

//es6中箭头函数
箭头函数的定义
箭头函数和普通函数相比的优势
  • 不绑定this和arguments
  • 更简洁的代码语法

说明:不绑定this

1
2
3
4
5
6
7
8
9
10
11
//箭头函数的this在定义的时候就已经确定了,之后不管什么时候调用,this都是之前定义时的this,

function Person(name,age){
this.name=name;
this.age=age;
setInterval(()=>{
console.log(`我的名字叫${this.name}我今年${this.age}岁`)
},3000)

}
let p=new Person('chenhang',20);

说明:不绑定arguments

1
2
3
4
5
//所以在箭头函数中不能直接使用arguments对象,
//如果需要获得函的参数又该怎么办呢?可以使用余参数来取代arguments

let func=(...args)=>console.log(args.length);
func(1,2,3,4,5,6) // 显示打印 6

箭头函数不适应的场景
  • 对象的方法中不建议使用箭头函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const Person={
    username:"陈子",
    age:20,
    say:()=>{
    console.log('111') //111
    setInterval(()=>{
    console.log(this.username+'----'+this.age)
    },2000) //undefined----undefined
    }

    }
    Person.say()

    /*
    输出结果有问题,因为方法 在了对象里,而对象的括号是不能封闭作用域的,所
    以此时的 this 还是指向全局对象,而全局对象下没有 usemame age 属性,所以会出现
    问题
    */
  • 不能作为构造函数

    1
    2
    3
    4
    /*
    由于箭头函数的 this 具有不绑定的特点,不能使用箭头函数作为构造函数,如果这
    样做了,也会报错。
    */
  • 定义原型方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    function Student(){
    this.username="成成"
    }

    Student.prototype.sayName=()=>{
    console.log(this.username) //报undefined
    }
    var p=new Student();
    p.sayName()
    /*
    出现问题的原因是 his 指向 window 象,这和使用箭头函数在对象中定义方法十
    分类似
    */

Map数据结构

js中的对象是键值对形式的集合(Hash结构),键只能是字符串类型

对象提供了’字符串-值’,Map提供了’值-值’的对应

创建Map
  • Map 可以接受 个数组作为参数,该数组的成员是 个个表示键值对的数组
    1
    2
    3
    4
    5
    const MapTree=new Map([
    ['name','chenhang'],
    ['age',20]
    ]);
    console.log(MapTree) // { 'name' => 'chenhang', 'age' => 20 }
Map常用的属性和方法
  • size返回Map的成员总数

    1
    console.log(MapTree.size)  //2
  • set 方法设置键名 key 对应的键值为 value ,然后返回整个 Map 结构。

  • 如果 key有对应的键值,则键值会被更新,否则就新生成键值。
  • set方法返回的是当前的 Map对象 因此可以采用链式写法。
1
2
3
4
5
6
7
8
9
MapTree.set('address',"上海市闵行区").set('sex','男');
console.log(MapTree)
/*
Map {
'name' => 'chenhang',
'age' => 20,
'address' => '上海市闵行区',
'sex' => '男' }
*/
  • has方法 放回一个布尔值判断某个键是否在当前的Map对象中

    1
    2
    console.log(MapTree.has('sex'))  //true
    console.log(MapTree.has('sex2')) //false
  • delete方法 删除键成功返true,删除失败返回false

    1
    2
    3
    console.log(MapTree.delete("address"))  //true
    console.log(MapTree)
    //Map { 'name' => 'chenhang', 'age' => 20, 'sex' => '男' }
  • 遍历方法

    Map结构提供三个遍历器生成函数和一个遍历方法

    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
    // keys()  返回所有键的遍历器
    const a=MapTree.keys();
    console.log(a) //MapIterator { 'name', 'age', 'sex' }

    for(let key of MapTree.keys()){
    console.log(key+"----这是key")
    }

    // values() 返回所有值的遍历器
    const b=MapTree.values();
    console.log(b) //MapIterator { 'chenhang', 20, '男' }

    for(let value of MapTree.values()){
    console.log(value+"----这是value")
    }
    //entries() 返回所有成员的遍历器
    const c=MapTree.entries();
    console.log(c) //MapIterator { [ 'name', 'chenhang' ], [ 'age', 20 ], [ 'sex', '男' ] }

    for (let [key,value] of MapTree.entries()){
    console.log(key,value)
    }
    /*
    name chenhang
    age 20
    sex 男
    */


    // forEach() 遍历Map所有的成员
    MapTree.forEach((value,index)=>{
    console.log(value+'--'+index);
    })
    /*
    chenhang--name
    20--age
    男--sex
    */

Module语法

es6之前社区使用CommonJS规范(服务器端)和AMD规范(浏览器端),
ES6 模块的设计思想是尽量静态化,使得编译时就能确定模块的依赖关系,以及输
入和输出的变量。

export
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//第1种写法
export var firstName = ’Michael';
export var lastName =’Jackson';
export var year= 1958;

//第2种写法
var firstName =’Michael’,
var lastName =’Jackson';
var year= 1958;
export { firstName, lastName, year } ;

//还可以导出函数
export function multiply(x, y) {
return x * y;
}
导出多个函数
function v1() { }
function v2() { }

export {v1,v2}
import
1
2
3
4
5
6
//main.js
import {firstName, lastName, year} from ./rofile js

function setName(element) {
element.textContent = firstName +’’+ lastName;
}
export default

使用 import 命令时,用户需要知道所要加载的变量名或函数名,否则将无法加载

1
2
3
4
5
6
7
// export-default.js
export default function () {
console.log('foo');
}
// import-default.js
import customName from './export-default
customName(); //foo

import 可以为匿名函数指定任意名字
,这时就不需要知道
原模块输出的函数名。需要注意的是,这时 import 令后面不使用大括号。

使用 export default 时,对 import
语句不需要使用大括号:不使用 export default 时,对应的 import 语句需要使用大括号。
export default 令用于指定模块的默认输出。显然 模块只能有 个默认输出,因此
expo default 令只能使用 所以 import 命令后面才不用加大括号,因为只能唯
对应 export default 命令

Promise对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const promise=new Promise(function(resolve,reject){
var value="成功值";
if(true){
resolve(value)
}else{
reject(error)
}
})

promise.then(function(value){
console.log(value)
})

/*
resolve 函数的作用是将 Promise 象的状态从“未完成”变为“成功”(即从 pending
变为 resolved ),在异步操作成功时调用,并将异步操作的结果作为参数传递出去。
reject 函数的作用是将 romise 象的状态从“未完成”变为“失败”(即从 pending
变为 ected ), 在异步操作失败时调用,并将异步操作的报错作为参数传递出去
*/

项目的路由配置

在 HTML5 history
API 出现之前,前端路由都通过 hash 实现, hash 能够兼容低版本的浏览器 如果把
节的 阳例子用 hash 来实现的话,它的 URI 规则中需要带上呀”。

什么是前端路由

1
2
3
4
/*
前端路由通过配置js文件,将这个工作拿到前端来完成。
路由就是根据不用的url来展示对应的内容和页面
*/

前端路由的使用场景

1
主要适用于单页面,spa基本是前后端分离,所以后端不会提供路由

前端路由优缺点

1
2
3
4
5
6
7
优点
1.没有网路延迟,用户体验提升,后端路由每次都会请求服务器然后响应
2.在某些场合中,用 JAX 请求,可以让页面无刷新,页面变了,但 URL 没变,
时不能复山时址阳路由支阳面应用叫地解二

缺点
使用浏览器的前进、后退按钮的时候会 新发送请求,没有合理地利用缓存

Vue-Router的使用

1.安装路由

1
2
3
4
5
6
7
8
9
10
cnpm install vue-router --save

//index.js

import Vue from 'vue';
import VueRouter from 'vue-router'
Vue.use(VueRouter);

// main.js
import router from "./router"

2.建立路由器模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// index.js
import Vue from 'vue';
import VueRouter from 'vue-router'

import Login from "@/components/Login"
import Home from "@/components/Home"

Vue.use(VueRouter);

export default new VueRouter({
routes:[
{
path:"/home",
name:"Home",
component:Home,
},{
path:"/login",
name:"Login",
component:Login
}

//.....
]
})

3.启动路由器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
在main.js入口文件中启用路由器,创建和挂载根实例,通过
router配置参数注入路由,从而让整个应用都有路由功能
*/

import Vue from "vue";
import App from "./App";
import router from "./router/index.js"
new Vue({
el:"#app",
router,
template:"<App/>",
components:{App}
})


// App.vue中对用的router-view会渲染

4.路由重定向

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
export default new VueRouter({
routes:[
{
path:"/",
redirect:"/home"
}
]
})

//重定向的目标也可以是一个命名的路由

export default new VueRouter({
routes:[
{
path:"/",
redirect:{name:'home'}
}
]
})


//重定向的目标甚至可以是一个方法,动态的返回重定向的目标

export default new VueRouter({
routes:[
{
path:"/",
redirect:to=>{
//方法接收“目标路由”作为参数
return //重定向的“字符串路径/路径对象”
}
}
]
})

5.路由懒加载

结合 Vue 的异步组件和 Webpack 的代码分割功能,可以轻松实现路由组件的懒加载

6.router-link

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<router-link :to="home">Home</route-link>

<!-- 同上--->
<router-link :to="{path:'home'}">HOme</router-link>

<!-- 命名的路由--->

<routet-link :to="{name:'user',params:{userID:123}}">User</router-link>

<!--带参数的查询--->
<router-link :to="{name:'register',query:{plan:'private'}}">Register</router-link>

<!--结果为/register?plan=private-->

7.路由的对象属性

  • $router.path

    字符串,对应当前路由的路径 总是解析为绝对路径,如“/foo/bar “

  • $router.params

    一个key/value对象,包含了动态片段和全匹配片段,如果没有路由参数 就为空对象

  • $router.query

    一个key/value对象,表示url的查询参数,/foo?id=1 表示$router.query.id==1,如果没有查询参数就为空对象

  • $router.hash

    当前路由的hash值(不带#),如果没有hash值则为空字符串

  • $router.fullPath

    完成解析后的 URL ,包含查询参数 hash 完整路径

  • $router.matched

    一个数组,包含当前路由的所有嵌套路径片段的路由记录

页面之间的导航

借助router实例的一些方法实现编程路由

$router.push
1
2
3
4
5
6
7
8
9
10
11
//参数可以是字符串路径
this.$router.push("user")

//参数可以是描述地址的对象
this.$router.push({path:"user"})

//命名的路由 #/user/123
this.$router.push({name:"User",params:{Id:123}})

//带参数的查询 页面跳转另一个页面需要携带数据可以使用此方法
this.$router.push({path:"user",query:{userAddress:'上海市'}})
$router.replace
1
2
3
4
5
6
7
/*
和push方法不同的是,他不会像history栈中添加新的记录,
而是跟他的方法名一样只替换掉当前的历史记录
*/
this.$router.replace("/user") //等价如下

<router-link :to="user" replace></router-link>
$router.go
1
2
3
4
this.$router.go(1) ==>  history.forward()
this.$router.go(-1) ==> history.back()

//如果记录不够就会失败

Vue.js 知识点

特点

解耦视图和数据

前端路由

可复用的组件

状态管理

Virtual DOM

MVVM特点

  • 低耦合
  • 可重用视图逻辑
  • 独立开发 专注于业务逻辑和数据的开发

Vue实例的生命周期

实例对象从构造函数时开始执行(被创建)到被GC回收销毁的整个存在的时期,在生命周期中被自动调用的函数称之为生命周期函数,也形象的称之为生命周期钩子函数

生命周期函数 含义
beforeCreate(创建前) 组件实例刚被创建,组件属性计算之前 比如data属性
created (创建后) 组件实例刚刚创建完成,属性已经绑定,此时DOM尚未生成,$el属性还不存在
beforeMount(载入前) 模板编译、挂载之前
mounted(挂载后) 模板编译、挂载之后
beforeUpdate(更新前) 组件更新之前
updated(更新后) 组件更新之后
beforeDestroy(销毁前) 组件销毁前调用
destroyed(销毁后) 组件销毁后调用
beforeCreate在组件刚刚被创建的时候增加一些loading事件
created调用时结束loading事件,完成一些初始化,实现函数自执行等
mounted是比较重要的函数,可以发起后端请求数据,接受页面之间传递的参数,由子组件向父组件传递参数等

class和style的绑定

当:class的表达式过长或逻辑复杂的时候,可以绑定一个计算属性,这是一种很友好和常见的做法

  1. 对象语法
    1
    2
    3
    4
    5
    6
    7
    <div :class="{active:isActive,....}"></div>

    data(){
    return{
    isActive:true
    }
    }
  1. 数组语法

    1
    2
    3
    4
    5
    6
    7
    8
    <div :class="[activeClass,errorClass]"></div>

    data(){
    return{
    activeClass:"active",
    errorClass:"static"
    }
    }
  2. 绑定内联样式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <div :style="{border:activeColor,fontSize:fontSize+'px'}"></div>


    data(){
    return{
    activeColor:"1px solid #ffc",
    fontSize:22
    }
    }

    //实际开发中写在data或者计算属性中

    <div :style="styleList"></div>

    data(){
    return{
    styleList:{
    border:"1px solid #fff",
    fontSize:22+"px"
    }
    }
    }

项目中与服务端通信

1.connect-mock-middleware工具的使用

  • config中的index.js中添加middleware,实际项目会换成后台提供的地址

    1
    2
    3
    4
    5
    6
    7
    proxyTable:{
    "/api":{
    target:"http://127.0.0.1:3721",
    changeOrigin:true,
    secure:false
    }
    }
  • 写mock文件

    1
    2
    3
    4
    5
    6
    7
    /*
    mock文件支持两种请求
    1.get/api/xxx
    2.post/api/<id>/123

    <id>代表路由链接表达式 如/api/:id/123 id的值会发生变化
    */

2.Mock.js语法

  • 优点
  1. 根据数据模板生成模拟数据
  2. 模拟Ajax请求返回模拟数据
  3. 基于html模板生成模拟数据
  • 语法规范
  1. 数据模板定义规范 (Data Template Definition—– DTD)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /*
    属性名 name
    生成规则 rule
    属性值 value
    'name|rule':value

    rule包括七种格式
    'name|min-max'
    'name|count'
    'name|min-max.dmin-dmax'
    'name|min-max.dcount'
    'name|count.dmin-dmax'
    'name|count.dcount'
    'name|+step'

    生成规则是可选的
    */
  2. 数据占位符定义规范(Data Placeholder Definition —–DPD)

3.snail mock工具的使用

  • 上面定义好了模拟的后台数据,现在需要通过生成的url来调用这些数据,snail mock可以模拟服务器的功能。生成的url

安装 cnpm isntall snail-cline -g

开启mock服务(在项目目录下打开cmd执行 ‘snail mock’ 命令会看到之前配置好的api地址)

1
2
3
经验:本地全局安装了snail-cline 其内部实现依赖很多其他的包,打开本地的package.json,
其中很多选项都是依赖于connect-mock-middleware的,依赖内部会自动下载管理依赖的包,同理
connect-mock-middleware中依赖mock,所以只需在本地全局安装snail-cline既可以使用mock语法

4.Axios安装和配置

项目服务端通信配置

1
2


前端模块化演变

发表于 2018-10-23

现状

前端产品的交付是基于浏览器,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。

  • 异步概念
    js是单线程的,由于执行ajax请求会消耗一定的时间,甚至出现了网络故障而迟迟得不到返回结果;这时,如果同步执行的话,就必须等到ajax返回结果以后才能执行接下来的代码,如果ajax请求需要1分钟,程序就得等1分钟。如果是异步执行的话,就是告诉ajax代码“老兄,既然你迟迟不返回结果,我先不等你了,我还有一大堆代码要执行,等你执行完了给我说一下”

  • 模块系统主要解决模块的定义、依赖和导出,先来看看已经存在的模块系统。

模块系统的演进

原始的 JavaScript 文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在 window 对象中,不同模块的接口调用都是一个作用域中,一些复杂的框架,会使用命名空间的概念来组织这些模块的接口,典型的例子如 YUI 库

这种原始的加载方式暴露了一些显而易见的弊端:

1.全局作用域下容易造成变量冲突

2.文件只能按照 <script> 的书写顺序进行加载

3.开发人员必须主观解决模块和代码库的依赖关系

4.在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪
​

CommonJS

服务器端的 Node.js 遵循 CommonJS规范,该规范的核心思想是允许模块通过 require 方法来同步加载所要依赖的其他模块,然后通过 exports 或 module.exports 来导出需要暴露的接口。

1
2
require("module");
module.exports = module;

优点:

  1. 服务器端模块便于重用
  2. NPM 中已经有将近20万个可以使用模块包
  3. 简单并容易使用

缺点:

  1. 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
  2. 不能非阻塞的并行加载多个模块

实现:

  1. 服务器端的 Node.js
  2. Browserify,浏览器端的 CommonJS 实现,可以使用 NPM 的模块,但是编译打包后的文件体积可能很大
  3. modules-webmake,类似Browserify,还不如 Browserify 灵活
  4. wreq,Browserify 的前身

AMD

Asynchronous Module Definition 规范其实只有一个主要接口 define(id?, dependencies?, factory),它要在声明模块的时候指定所有的依赖 dependencies,并且还要当做形参传到 factory 中,对于依赖的模块提前执行,依赖前置。

1
2
3
4
define("module", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });

优点:

  • 适合在浏览器环境中异步加载模块
  • 可以并行加载多个模块
  • 缺点:
  • 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不顺畅
  • 不符合通用的模块化思维方式,是一种妥协的实现

实现:

  • RequireJS
  • curl

CMD

Common Module Definition 规范和 AMD 很相似,尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。

1
2
3
4
5
6
define(function(require, exports, module) {
var $ = require('jquery');
var Spinning = require('./spinning');
exports.doSomething = ...
module.exports = ...
})

优点:

  • 依赖就近,延迟执行
  • 可以很容易在 Node.js 中运行

缺点:

  • 依赖 SPM 打包,模块的加载逻辑偏重

实现:

  • Sea.js
  • coolie

UMD

Universal Module Definition 规范类似于兼容 CommonJS 和 AMD 的语法糖,是模块定义的跨平台解决方案。

ES6 模块

ECMAScript6 标准增加了 JavaScript 语言层面的模块体系定义。ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

1
2
3
import "jquery";
export function doStuff() {}
module "localModule" {}

优点:

  • 容易进行静态分析
  • 面向未来的 ECMAScript 标准

缺点:

  • 原生浏览器端还没有实现该标准
  • 全新的命令字,新版的 Node.js才支持

实现:

  • Babel

前端模块加载

前端模块要在客户端中执行,所以他们需要增量加载到浏览器中。
模块的加载和传输,我们首先能想到两种极端的方式,一种是每个模块文件都单独请求,另一种是把所有模块打包成一个文件然后只请求一次。显而易见,每个模块都发起单独的请求造成了请求次数过多,导致应用启动速度慢;一次请求加载所有模块导致流量浪费、初始化过程慢。这两种方式都不是好的解决方案,它们过于简单粗暴。
分块传输,按需进行懒加载,在实际用到某些模块的时候再增量更新,才是较为合理的模块加载方案。
要实现模块的按需加载,就需要一个对整个代码库中的模块进行静态分析、编译打包的过程。

所有资源都是模块

在上面的分析过程中,我们提到的模块仅仅是指JavaScript模块文件。然而,在前端开发过程中还涉及到样式、图片、字体、HTML 模板等等众多的资源。这些资源还会以各种方言的形式存在,比如 coffeescript、 less、 sass、众多的模板库、多语言系统(i18n)等等。
如果他们都可以视作模块,并且都可以通过require的方式来加载,将带来优雅的开发体验,比如:

1
2
3
4
require("./style.css");
require("./style.less");
require("./template.jade");
require("./image.png");

如何使用reuqire管理

静态分析

在编译的时候,要对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,然后将不同类型的模块提交给适配的加载器来处理。比如一个用 LESS 写的样式模块,可以先用 LESS 加载器将它转成一个CSS 模块,在通过 CSS 模块把他插入到页面的

开通博客

发表于 2018-10-20
  • 在生成以及部署文章之前,需要安装一个扩展:npm install hexo-deployer-git –save

  • 新建博客

    1
    hexo new post "博客名"
  • 或者直接在_posts下新建md文件

  • hexo d -g生成以及部署

    ​

Eastboat

Eastboat

love coding, love life

4 日志
2 标签
© 2020 Eastboat
由 强力驱动
本站访客数:
|
主题 — v5.1.4