使用Vue模板
本文最后更新于:2021年4月12日 晚上
创建模板项目
vue-cli:官方提供的脚手架工具
从 vuejs-templates 下载模板项目
- 创建 vue 项目
1
2
3
4
5
6npm install -g vue-cli
vue init webpack vue_demo #名称不能包含大写
# vue-router, unit tests 和 e2e tests 两个单元测试包暂时不需要,其余默认
cd vue_demo
npm run dev
# 访问: http://localhost:8080/ - 模板项目的结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17|-- build : webpack 相关的配置文件夹(基本不需要修改)
|-- dev-server.js : 通过 express 启动后台服务器
|-- config: webpack 相关的配置文件夹(基本不需要修改)
|-- index.js: 指定的后台服务的端口号和静态资源文件夹
|-- src : 源码文件夹
|-- components: vue 组件及其相关资源文件夹
|-- App.vue: 应用根主组件
|-- main.js: 应用入口 js
|-- static: 静态资源文件夹
|-- .babelrc: babel 的配置文件
|-- .eslintignore: eslint 检查忽略的配置
|-- .eslintrc.js: eslint 检查的配置
|-- .gitignore: git 版本管制忽略的配置
|-- index.html: 主页面文件
|-- package.json: 应用包配置文件
|-- README.md: 应用描述说明的 readme 文件
|-- node_modules
基本使用
vue文件的组成
分为3部分:模板页面,JS 模块对象,样式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<template>
// 页面模板
</template>
<script>
export default {
data() {
return {}
},
methods: {},
computed: {},
components: {}
}
</script>
<style>
/* 样式定义 */
</style>简单案例
1
2
3
4
5
6
7
8
9<!-- 结构目录 -->
|-- src
|-- assets
|-- logo.png
|-- components
|-- helloworld.vue
|-- App.vue
|-- main.js
|-- index.html1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// helloworld.vue
<template>
<div>
<p class="msg">{{ msg }}</p>
</div>
</template>
<script>
export default { //配置对象(与Vue一致)
data() { //必须写函数
return {
msg: 'Hello Vue Component',
}
},
}
</script>
<style>
.msg {
color: red;
font-size: 30px;
}
</style>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// App.vue
<template>
<div>
<img class="logo" src="./assets/logo.png" alt="logo" />
<!-- 3. 使用组件标签 -->
<HelloWorld />
</div>
</template>
<script>
// 1. 引入组件
import HelloWorld from './components/helloworld'
export default {
// 2. 映射组件标签
components: {
HelloWorld,
},
}
</script>
<style>
.logo {
width: 200px;
height: 200px;
}
</style>1
2
3
4
5
6
7
8
9
10// main.js
// 入口JS:创建Vue实例
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: "#app", //index.html里写的名称
components: { App }, //组件标签名
template: '<App/>' // 模板插入到el所匹配的页面div里
})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>helloworld</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
项目打包与发布
- 静态服务器工具包
1
2
3
4
5
6# 打包,会生成dist文件夹
npm run build
# 发布
npm install -g serve
serve dist
# 访问:http://localhost:5000 - 动态 web 服务器(tomcat)
- 修改配置
1
2
3
4
5// webpack.prod.conf.js
output: {
publicPath: '/xxx/'
//打包文件夹的名称
} - 重新打包:
npm run build
- 修改 dist 文件夹为项目名称: xxx
- 将 xxx 拷贝到运行的 tomcat 的 webapps 目录下
- 访问: http://localhost:8080/xxx
- 修改配置
组件间通信
props
- 使用组件标签时
<my-component name='tom' :age='3' :set-name='setName' />
- 定义 MyComponent 时
在组件内声明所有的props- 只指定名称
props: ['name', 'age', 'setName']
- 指定名称和类型
1
2
3
4
5props: {
name: String,
age: Number,
setNmae: Function
} - 指定名称/类型/必要性/默认值
1
2
3
4props: {
name: {type: String, required: true, default:xxx},
age: {type: Number, required: true, default:xxx}
}
- 只指定名称
- 注意
- 此方式用于父组件向子组件传递数据
- 所有标签属性都会成为组件对象的属性, 模板页面可以直接引用
- 问题:
- 如果需要向非子后代传递数据必须多层逐层传递
- 兄弟组件间也不能直接 props 通信, 必须借助父组件才可以
vue自定义事件
- 绑定事件监听
- 通过 v-on 绑定
1
2<!-- 给TodoHeader标签对象绑定addTodo事件监听 -->
<TodoHeader @addTodo="addTodo" /> - 通过 $on()
1
2
3
4
5
6<template>
<TodoHeader ref="header" />
</template>
<script>
this.$refs.header.$on('addTodo', this.addTodo)
</script>
- 通过 v-on 绑定
- 触发事件
1
2// 触发事件(只能在父组件中接收)
this.$emit('addTodo', todo)
- 注意
- 此方式只用于子组件向父组件发送消息(数据)
- 问题: 隔代组件或兄弟组件间通信此种方式不合适
消息订阅与发布
- 安装 PubSubJS 库
npm i --save pubsub-js
绑定事件监听 —订阅消息
触发事件 —发布消息
- 订阅消息
PubSub.subscribe('msg', function(msg, data){})
- 发布消息
PubSub.publish('msg', data)
- 注意
优点: 此方式可实现任意关系组件间通信(数据) - 总结: 事件的 2 个重要操作
- 绑定事件监听 (订阅消息)
目标: 标签元素<button>
事件名(类型): click/focus
回调函数:function(event){}
- 触发事件 (发布消息)
DOM 事件: 用户在浏览器上对应的界面上做对应的操作
自定义: 编码手动触发
- 绑定事件监听 (订阅消息)
slot
此方式用于父组件向子组件传递标签,而非数据。
- 子组件 Child.vue
1
2
3
4
5
6
7<template>
<div>
<slot name="xxx">不确定的标签结构 1</slot>
<div>组件确定的标签结构</div>
<slot name="yyy">不确定的标签结构 2</slot>
</div>
</template> - 父组件 Parent.vue
1
2
3
4<child>
<div slot="xxx">xxx 对应的标签结构</div>
<div slot="yyy">yyyy 对应的标签结构</div>
</child>
vue-ajax
vue-resource
vue 插件, 非官方库, vue1.x 使用广泛。文档
下载 npm install vue-resource --save
1 |
|
axios
通用的 ajax 请求库, 官方推荐, vue2.x 使用广泛。
下载 npm install axios --save
1 |
|
UI 组件库
- Mint UI:
主页: http://mint-ui.github.io/#!/zh-cn
说明: 饿了么开源的基于 vue 的移动端 UI 组件库 - Elment
主页: http://element-cn.eleme.io/#/zh-CN
说明: 饿了么开源的基于 vue 的 PC 端 UI 组件库
配置
1 |
|
1 |
|
使用
mint-ui 组件分为 标签组件 与 非标签组件
1 |
|
1 |
|
1 |
|
1 |
|
vue-router
官方提供的用来实现 SPA 的 vue 插件。文档
下载npm install vue-router --save
路由组件
- VueRouter(): 用于创建路由器的构建函数
1
2
3new VueRouter({
// 多个配置项
}) - 路由配置
1
2
3
4
5
6
7
8
9
10routes: [
{ // 一般路由
path: '/about',
component: About
},
{ // 自动跳转路由
path: '/',
redirect: '/about'
}
] - 注册路由器
1
2
3
4import router from './router'
new Vue({
router
}) - 使用路由组件标签
<router-link>
: 用来生成路由链接
<router-link to="/xxx">Go to XXX</router-link>
<router-view>
: 用来显示当前路由组件界面
<router-view></router-view>
向路由组件传递数据
路由传参(param)
- 配置路由
1
2
3
4
5
6children: [
{
path: 'mdetail/:id',
component: MessageDetail
}
] - 路由路径
<router-link :to="'/home/message/mdetail/'+m.id">{{m.title}}</router-link>
- 路由组件中读取请求参数
this.$route.params.id
- 配置路由
路由传参(query)
- 在地址的后面 写上 ?分隔
- 通过 key=value&key2=value 的方式添加参数
- 组件中通过
this.$route.query
访问对应的 key 即可获取数据
属性携带数据 <router-view :msg="msg"></router-view>
缓存路由组件对象
- 默认情况下, 被切换的路由组件对象会死亡释放, 再次回来时是重新创建的
- 如果可以缓存路由组件对象, 可以提高用户体验
1
2
3<keep-alive>
<router-view></router-view>
</keep-alive>
相关 API
- this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
- this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
- this.$router.back(): 请求(返回)上一个记录路由
- this.$router.go(-1): 请求(返回)上一个记录路由
- this.$router.go(1): 请求下一个记录路由
前端history路由的问题
页面中加载的资源相对路径会根据地址栏的变化而变化,所以要使用绝对路径,或相对于根目录的路径需要声明base属性,以说明地址中哪部分是前端路由部分,哪部分是后端路由部分。
以特定url开头的地址都需要返回该url对应的内容。
该模式不利于缓存。
vuex
了解
文档
简单来说: 对 vue 应用中多个组件的共享状态进行集中式的管理(读/写)
- 状态自管理应用
state: 驱动应用的数据源
view: 以声明方式将 state 映射到视图
actions: 响应在 view 上的用户输入导致的状态变化(包含 n 个更新状态的方法)【一个action是一个函数】 - 多组件共享状态的问题
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
- 以前的解决办法
- 将数据以及操作数据的行为都定义在父组件
- 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
- vuex 就是用来解决这个问题的
核心概念和 API
state
- vuex管理的状态对象
- 它应该是唯一的
1
2
3const state = {
xxx: initValue
}
mutations
- 包含多个直接更新 state 的方法(回调函数)的对象
- 谁来触发:action 中的 commit(‘mutation 名称’)
- 只能包含同步的代码,不能写异步代码
1
2
3
4
5const mutations = {
yyy(state, {data1}){
// 更新 state 的某个属性
}
}
actions
- 包含多个事件回调函数的对象
- 通过执行: commit()来触发 mutation 的调用, 间接更新 state
- 谁来触发: 组件中: $store.dispatch(‘action 名称’, data1) // ‘zzz’
- 可以包含异步代码(定时器, ajax)
1
2
3
4
5const action = {
zzz({commit, state}, data1){
commit('yyy', {data1})
}
}
getters
- 包含多个计算属性(get)的对象
- 谁来读取: 组件中: $store.getters.xxx
1
2
3
4
5const getters = {
mmm(state){
return ...
}
}
store 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 向外暴露 store 对象
// store.js
export default new Vuex.Store({
state, // 状态
getters, // 包含多个getter计算属性函数的对象
mutations, // 包含多个更新state函数的对象
actions, // 包含多个对应事件回调函数的对象
}
// 映射 store
// main.js
import store from './store'
new Vue({
store
})- 所有用 vuex 管理的组件中都多了一个属性$store, 它就是一个 store 对象
- 属性:
- state: 注册的 state 对象
- getters: 注册的 getters 对象
- 方法:
- dispatch(actionName, data): 分发调用 action
组件中简化语法
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// App.vue
import {mapState, mapGetters, mapActions} from 'vuex'
export default {
/* computed: {
count () {
return this.$store.state.count
},
evenOrOdd () {
return this.$store.getters.evenOrOdd
}
}, */
computed: {
...mapState(['count']),
...mapGetters(['evenOrOdd'])
},
/*methods: {
increment () {
this.$store.dispatch('increment')
},
decrement () {
this.$store.dispatch('decrement')
}
} */
methods: {
...mapActions(['increment', 'decrement'])
}
}modules
- 包含多个 module
- 一个 module 是一个 store 的配置对象
- 与一个组件(包含有共享数据)对应
例子
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
render
1 |
|
上述代码可以简写为:
1 |
|
其中,render函数相当于:
1 |
|
web前端学习笔记14——使用Vue模板
http://example.com/posts/5f4b0425.html