Vue,中国的 Web 前端框架!

Vue的两个特性

1、数据驱动视图

2、双向数据绑定

底层原理:MVVM(Module 数据源、View 视图、ViewModel 是 Vue 的实例)

指令

1、内容渲染

支持 JavaScript 表达式的运算

1
2
{{ number + 1}}
{{ tips.split('').reverse().join('') }} //反转字符串
  • v-text 覆盖元素内部原有的内容

  • 插值表达式

  • v-html 渲染包含 HTML 标签的字符串

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <div id="app">
    <p>姓名:{{ tom }}</p>
    <h2 v-html="message2"></h2>
    </div>
    <script>
    const app = new Vue({
    el: '#app',//控制的区域
    data: {//所要渲染的数据
    tom: "王狗",
    message2: '<h2 style="color: red">hegou</h2>'
    }
    });
    </script>

2、属性绑定

注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中

  • v-bind

    简写为冒号:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <div id="app">
    <input type="text" :placeholder="tips">
    </div>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    tips: "请输入"
    }
    });
    </script>

3、事件绑定

  • 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
    <div id="app">
    <p>{{ count }}</p>
    <button @click="add($event, 1)">+N</button><br
    </div>

    <script>
    const app = new Vue({
    el: '#app',
    data: {
    count: 0
    },
    methods: {
    add(e, n) {
    this.count += n
    if (this.count % 2 == 0) {
    e.target.style.backgroundColor = 'red'
    } else {
    e.target.style.backgroundColor = null
    }
    }
    },
    });
    </script>
  • 事件修饰符

    1
    2
    @click.prevent <!-- 阻止默认行为 -->
    @click.stop <!-- 阻止事件冒泡 -->
  • 按键修饰符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <body>
    <div id="app">
    <input type="text" @keyup.esc="clear" @keyup.enter="submit">
    </div>

    <script>
    const app = new Vue({
    el: '#app',
    methods: {
    clear(e) {
    e.target.value = ''
    },
    submit(e) {
    console.log('触发enter提交事件')
    }
    },
    });
    </script>
    </body>

4、双向绑定

  • v-model

    使用在表单元素:

    1、input

    2、textarea

    3、select

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <body>
    <div id="app">
    <p>用户名:{{ username }}</p>
    <input type="text" v-model="username"><br>
    <select v-model="city">
    <option value="1">上海</option>
    <option value="2">深圳</option>
    <option value="3">广州</option>
    </select>
    </div>

    <script>
    const app = new Vue({
    el: '#app',
    data: {
    username: 'zou',
    city: '3'
    },
    });
    </script>
    </body>

    专用修饰符:

    .number 自动将用户的输入值转为数值类型

    .trim 自动过滤用户输入的首尾空白字符

    .lazy 在 “change” 时而非 “input” 时更新

    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
    <body>
    <div id="app">
    <input type="text" v-model.number="n1"> +
    <input type="text" v-model.number="n2"> =
    <span>{{ n1 + n2 }}</span><br>
    <input type="text" v-model.trim.lazy="username">
    <button @click="showName">获取用户名</button>
    </div>

    <script>
    const app = new Vue({
    el: '#app',
    data: {
    username: 'zou',
    n1: 1,
    n2: 2
    },
    methods: {
    showName() {
    console.log(`用户名是:"${this.username}"`)
    }
    },
    });
    </script>
    </body>

5、条件渲染

  • v-if

    原理:每次动态创建或移除元素

  • v-show

    原理:动态为元素添加或移除 display:none 样式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <body>
    <div id="app">
    <p v-if="flag">被 v-if 控制的元素</p>
    <p v-show="flag">被 v-show 控制的元素</p>

    <p v-if="type === 'A'">优秀</p>
    <p v-else-if="type === 'B'">良好</p>
    <p v-else>一般</p>
    </div>

    <script>
    const app = new Vue({
    el: '#app',
    data: {
    flag: true,
    type: 'A'
    },
    });
    </script>
    </body>

6、列表渲染

  • v-for

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <body>
    <div id="app">
    <table class="table table-bordered table-hover table-striped">
    <thead>
    <th>索引</th>
    <th>id</th>
    <th>姓名</th>
    </thead>
    <tbody>
    <!-- 把数据项的 id 作为 key 的值,因为具有唯一性 -->
    <tr v-for="(item, index) in list" :key="item.id">
    <td>{{ index + 1 }}</td>
    <td>{{ item.id }}</td>
    <td>{{ item.name }}</td>
    </tr>
    </tbody>
    </table>
    </div>

    <script>
    const app = new Vue({
    el: '#app',
    data: {
    list: [
    { id: 1, name: '王狗'},
    { id: 2, name: '小何'},
    { id: 3, name: 'laofu'}
    ]
    },
    });
    </script>
    </body>

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<div id="app">
<form @submit.prevent="add">
<input type="text" placeholder="请输入" v-model.trim="brand">
<button type="submit">添加</button>
</form><br>

<table class="table table-bordered table-hover table-striped">
<thead>
<th>索引</th>
<th>id</th>
<th>姓名</th>
<th>智商</th>
<th>操作</th>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<div>
<input type="checkbox" :id="'cb' + item.id" v-model="item.status">
<label :for="'cb' + item.id" v-if="item.status">打开</label>
<label :for="'cb' + item.id" v-else>关闭</label>
</div>
</td>
<td>
<a href="" @click.prevent="remove(item.id)">删除</a>
</td>
</tr>
</tbody>
</table>
</div>

<script>
const app = new Vue({
el: '#app',
data: {
list: [
{ id: 1, name: '王狗', status: true },
{ id: 2, name: '小何', status: false },
{ id: 3, name: 'laofu', status: true }
],
brand: "",
nextId: 4 //下一个可用的 id
},
methods: {
remove(id) {
this.list = this.list.filter(item => item.id !== id)
},
add() {
if (this.brand === '') {
return alert('必须填写')
}
const obj = {
id: this.nextId,
name: this.brand,
status: true
}
this.list.push(obj)
this.brand = ''
this.nextId++
}
},
});
</script>

全局过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<p>{{ time | dateFormat }}</p>
</div>

<script>
Vue.filter('dateFormat', (time) => {
return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
})

const app = new Vue({
el: '#app',
data: {
time: new Date()
},
});
</script>

侦听器

作用:监视数据的变化

1、方法格式

缺点:1)无法在刚进入页面时触发 2)若监听的是对象,对象中属性变化时无法触发

1
2
3
4
5
6
username(newval, oldval) {
if (newval === '') return
$.get('http://zouweiyi.com' + newval, (result) => {
console.log(result)
})
}

2、对象格式

1
2
3
4
5
6
7
info: {
handler(newval, oldval) {
console.log(newval, oldval)
},
immediate:true,//刚进入页面时触发
deep: true//深度监听
}
  • deep 的代替方案

    1
    2
    3
    4
    5
    watch: {
    'info.username'(newval, oldval) {
    console.log(newval, oldval)
    }
    }

计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div id="app">
<input type="text" v-model.number="r"><br>
<input type="text" v-model.number="g"><br>
<input type="text" v-model.number="b">
<div class="box" :style="{ backgroundColor: rgb }">
{{ rgb }}
</div>
</div>

<script>
const app = new Vue({
el: '#app',
data: {
r: 0,
g: 255,
b: 0
},
computed: {
rgb() {
return `rgb(${this.r}, ${this.g}, ${this.b})`
}
}
});
</script>

axios

  • post

    1
    2
    3
    4
    5
    6
    7
    8
    9
    axios({
    method: 'post',
    url: 'http://www.liulongbin.top:3006/api/post',
    data: {
    id: 1
    }
    }).then(result => {
    console.log(result)
    })
  • get

    1
    2
    3
    4
    5
    6
    7
    8
    9
    axios({
    method: 'get',
    url: 'http://www.liulongbin.top:3006/api/getbooks',
    params: {
    id: 1
    },
    }).then(result => {
    console.log(result.data)
    })

vue-cli

image-20220713223340632

image-20220713223520617

assets: 静态文件(css)

components: 可复用的组件

main.js: 项目的入口文件

App.vue: 项目的根组件

流程:

main.js 把 App.vue 渲染到 index.html 的指定区域中

vue 组件

template: 组件的模板结构

style: 组件的 JavaScript 行为

script: 组件的样式

注册私有组件:

(1) 在 script 中导入(import)

(2)在 export default 的 components 中注册标签

(3)在 template 中使用标签

注册全局组件:

(1)导入: import Right from './components/Right.vue'

(2)注册标签: Vue.component('Left', Left) //第一个参数为注册的标签名

(3)在其它组件中使用标签

组件的 props(为了提高组件的复用性)

自定义属性

props 应该是只读的

样式冲突

1
<style lang="less" scoped>
  • 使用第三方组件库,修改默认样式

  • 父组件修改子组件的样式

    1
    2
    3
    /deep/h3 {
    color: red;
    }

生命周期 & 数据共享

组件的生命周期

指一个组件从创建 --> 运行 --> 销毁

lifecycle

组件之间的数据共享

ref 引用

动态组件

component 是内置标签,是组件的占位符,动态切换组件的显示与隐藏

1
2
3
<keep-alive>
<component :is="conName"></component>
</keep-alive>

使用 keep-alive 标签让组件被缓存而不是销毁,keep-alive 内置 activated 和 deactivated

当组件第一次被创建,既会执行 created 也会执行 activated

当组件被激活,只会执行 activated

keep-alive 标签的 include属性:指定哪些组件被缓存;exclude属性:指定哪些组件不被缓存;不能同时使用。

==注意==

components 中的组件名作用是使用到页面结构中

而原组件中的 name 作用是结合 keep-live 指定哪个组件被缓存或不被缓存

插槽

每个 slot 插槽,都有 name 属性

template 只是一个虚拟标签,v-slot 必须用在 template 上

v-slot 指令简写为 #

把内容填充到指定名称的插槽中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<Left>
<template v-slot:s1>
<p>你好</p>
</template>
<template #s2>
<p>香菜</p>
</template>
</Left>

Left.vue:
<template>
<div class="left-container">
<h3>Left 组件</h3>
<slot name="s1"></slot>
<slot name="s2">s2的默认内容</slot>
</div>
</template>

作用域插槽

1
2
3
4
5
6
7
8
9
<Article>
<template #content="{ msg, username }"> // 解构赋值
{{ msg }}
{{ username }}
</template>
</Article>

Article.vue:
<slot name="content" msg="hellovue" :username="name"></slot>

自定义指令

  • 私有自定义指令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <h1 v-color="color">App 根组件</h1>
    <h1 v-color="'pink'">App 根组件</h1>
    <button @click="color = 'green'">改变 color</button>

    data() {
    return {
    color: "blue",
    };
    },
    directives: {
    color: {
    //bind 函数只会在一开始触发一次
    bind(el, binding) {
    el.style.color = binding.value;
    },
    //updata 函数在数据更新时触发
    update(el, binding) {
    el.style.color = binding.value;
    }
    },
    },
  • 全局自定义指令

    1
    2
    3
    4
    5
    6
    7
    8
    Vue.directive('color', {
    bind(el, binding) {
    el.style.color = binding.value;
    },
    update(el, binding) {
    el.style.color = binding.value;
    }
    })

路由

嵌套路由

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
App.vue:
<router-link to="/home">首页</router-link>
<router-link to="/movie">电影</router-link>
<router-link to="/about">关于</router-link>

<router-view></router-view>

About.vue:
<router-link to="/about">tab1</router-link>
<router-link to="/about/tab2">tab2</router-link>

<router-view></router-view>

index.js:
routes: [
{ path: '/', redirect: '/home' }, // 重定向
{ path: '/home', component: Home },
{ path: '/movie', component: Movie },
{
path: '/about', component: About, children: [
{ path: '', component: Tab1 }, // path 为空字符串,默认子路由
{ path: 'tab2', component: Tab2 },
]
},
]

动态路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
App.vue
<router-link to="/movie/1">电影1</router-link>
<router-link to="/movie/2">电影2</router-link>
<router-link to="/movie/3">电影3</router-link>

Movie.vue
<template>
<div class="movie-container">
<h3>Movie 组件 -- {{ id }}</h3>
</div>
</template>

<script>
export default {
name: "Movie",
props: ["id"],
};
</script>

index.js
{ path: '/movie/:id', component: Movie, props: true }, // 开启 props 传参

导航

this.$route 是参数对象,用 this.$route.params 访问路径参数,用 this.$route.query 访问查询参数

this.$router 是导航对象

  • 声明式导航

    点击链接实现导航

  • 编程式导航

    调用 API 方法

    ① this.$router.push(‘’) 跳转到指定页面(产生历史记录)

    ② this.$router.replace(‘’) 跳转到指定页面(不产生历史记录)

    ③ this.$router.go(n) 前进或后退 n 步到历史页面

    • this.$router.back() 后退一步
    • this.$router.forward() 前进一步

导航守卫

控制路由的访问权限