vue 从入门到实践开发

Vue 官网

选项 / 数据

props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
Vue.component('child',{
props:['content'],
props:{
content: Number
content: [Number,String],
content: {
type: String,
required:true,
default:'default value'
},
validator: function(value){
return value.length > 5
}
},
template:"<div>{{content}}</div>"
})
</script>

Prop

  • Prop 大小写
  • 静态动态Prop传递、Prop 类型及Prop 验证
  • 单向数据流
  • 非 Prop 的继承和禁用继承

自定义事件

  • 事件名
  • 自定义组件的 v-model(model)
  • 原生事件绑定到组件(.native、$listeners-表单类元素)
  • .sync修饰符-实现类似双向绑定效果
1
2
3
<div id="root">
<child @click.native="handleClick"></child>
</div>

选项 / DOM

el

选项 / 组合

mixins

实例 property

vm.$slots

实例方法 / 数据

vm.$watch

vm.$set

vm.$delete

实例方法 / 事件

vm.$emit

this.$parent

this.$children

this.$root

指令

常用指令

v-if
v-show
v-for

1
2
// :key 标识组件的唯一性
// 操作数组更新视图的 push、pop、shift、unshift、splice、sort、reverse 或改变引用地址

v-bind
v-model
v-text
v-html

v-once

只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

1
2
3
4
5
6
7
8
9
10
11
12
/* 通过 v-once 创建低开销的静态组件
渲染普通的 HTML 元素在 Vue 中是非常快速的,但有的时候你可能有一个组件,这个组件包含了大量静态内容。在这种情况下,你可以在根元素上添加 v-once attribute 以确保这些内容只计算一次然后缓存起来,就像这样:
*/

Vue.component('terms-of-service', {
template: `
<div v-once>
<h1>Terms of Service</h1>
... a lot of static content ...
</div>
`
})

组件封装

1
2
3
4
5
6
7
8
9
10
11
12
<!--ref refs 的使用-->
<table>
<tr is="row"></tr>
</table>
<ul>
<li is="row"></li>
</ul>
<script>
Vue.component('row',{
template:'<tr><td>this is a row<td><tr>'
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--ref refs 的使用-->
<div ref="hello" @click="handleClick">
hello world
<counter ref="one" @change="handleChange"></counter>
</div>
<script>
var vm = new Vue({
el:'#root',
methods:{
handleClick:function(){
this.$refs.hello // 获取 Dom 节点
this.$refs.one // 获取 子组件的引用
}
}
})
</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
// 非父子组件间传值(Bus/总线/发布订阅模式/观察者模式)
<div id="root">
<child content="Dell"></child>
<child content="Lee"></child>
</div>
<script>
Vue.prototype.bus = new Vue()
Vue.component('child',{
data: function(){
return {
selfContent: this.content
}
},
props:{
content:String
},
template:'<div @click="handClick">{{selfContent}}</div>',
methods:{
handleClick: function(){
this.bus.$emit('change',this.selfContent)
}
},
mounted:function(){
var this_ = this
this.bus.$on('change',function(msg){
this_.selfContent = msg
})
}
})
</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
<body>
<div id="root">
<child>
<template slot-scope="props">
<h1>
{{props.item}}
</h1>
</template>
</child>
</div>
</body>
<script>
Vue.component('child',{
data:function(){
return {
list:[1,2,3,4]
}
},
template:`
<div>
<ul>
<slot v-for="item of list" :item=item>
</slot>
</ul>
</div>
`
})
</script>

属性选择器[class*=’hm-icon-‘]

全局组件、局部组件
父子组件传值,非父子组件传值
全部样式、局部样式
样式绑定、vuecli vue create ui 界面

组件化
独立的、可复用的

组件化
实现功能模块的复用
高执行效率
开发单页面复杂应用

如何拆分
300行原则
复用原则
业务复杂性原则

组件化带来的问题
组件状态管理(vuex)
多组件的混合使用、多页面、复杂业务(vue-router)
组件间的传参、消息(props,emit/on )

风格指南

vue-router
vuex

集成 vue
单页面、多页面引入Vue.js
复杂单页面应用 Vue cli 工具

vue.config 配置 webpack
部署

vuecli2 vuecli3

npm root -g 全局安装目录
npm i -S
npm i -D
npm init -y ? = npm init -f

vuerouter
routerview
routerlink
router children

hash、history

$emit :传方法

PubSubJS 订阅发布

1
2
3
4
5
6
7
8
9
10
11
// 订阅
import PubSub from 'pubsub-js'
mounted(){
PubSub.subscribe('add',(msg,data)=>{
this.addMenu(data);
})
}

// 发布
import PubSub from 'pubsub-js'
PubSub.publist('add',menu)

vue-resource axios

过渡动画 transition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
.v-enter,
.v-leave-to{
opacity: 0;
}
.v-enter-active,
.v-leave-active{
transition: opacity 3s;
}
</style>
<div id="root">
<transition>
<div v-if="show">
hello world
</div>
</transition>
</div>

动画

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
<style>
@keyframes bounce-in {
0% {
transform: scale(0)
}
50% {
transform: scale(1.5)
}
100% {
transform: scale(1)
}
}
.fade-enter-active {
transform-origin: left center;
animation: bounce-in 1s;
}
.fade-leave-active {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
</style>
<transition name="fade">
<div v-show="show">
keyframes
</div>
</transition>

redirect

css router-link-active

路由嵌套

keep-alive

路由传参

模拟数据

vue.config.js derServer before

指令封装

Vue 核心知识

Vue.component 缺点

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
<body>
<div id="app">
<ol>
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id"
></todo-item>
</ol>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})

var app = new Vue({
el: '#app',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
})
</script>
</body>
  • 全局定义:强制要求每个 component 中的命名不得重复
  • 字符串模板:缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 \
  • 不支持 CSS:意味着当 HTML 和 JavaScript 组件话时,CSS 明显被遗漏
  • 没有构建步骤:限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug(formerly Jade)和 Babel

.vue 单文件组件的优势

  • 完整语法高亮
  • CommonJS 模块
  • 组件作用域 CSS

核心概念

Vue 组件 = Vue 实例 = new Vue(options)

  • 属性
  • 事件
  • 插槽

组件的组成-属性

推荐组件属性传值写法

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
<script>
export default{
name: 'PropsDemo',
inheritAttrs: false, // title 会自动挂载到元素上 ,false 会取消自动挂载
props:{
name: String,
type:{
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ['success','warning','danger'].includes(value);
}
},
list:{
type: Array,
// 对象或数组必须从一个工厂函数获取
default: () => []
},
isVisible:{
type: Boolean,
default: false
},
item: {
type: Object,
default: () => ({}),
},
}
}
</script>

组件的组成-事件

阻止冒泡
.stop 修饰符
e.stopPropagation();

组件的组成-插槽

推荐使用 v-slot

双向绑定 和 单向数据流

双向绑定:model 的更新触发 view 的更新,同时 view 的更新也会触发 model 的更新。
单向数据流:model 的更新触发 view 的更新

双向绑定 or 单向数据流

  • Vue 是单向数据流,不是双向绑定
  • Vue 的双向绑定不过是语法糖
  • Object.defineProper 是用来做响应式更新的,和双向绑定没关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<PersonalInfo v-model="phoneInfo" :zip-code.sync="zipCode"></PersonalInfo>

// 最终编译
<PersonalInfo :phone-info="phoneInfo" :zip-code="zipCode" @change="val => (phoneInfo = val)" @update:zipCode="val => (zipCode = val)"></PersonalInfo>
</template>

<script>
export default{
model:{
prop: "phoneInfo", // 默认 value
event: "change" // 默认 input
}
}
</script>

虚拟 DOM 及 key 属性的作用

key 有利于虚拟 DOM 的复用

触发组件的更新

数据驱动

数据来源(单向的)

  • 来自父元素的属性
  • 来自组件的状态 data
  • 来自状态管理器,如 Vuex、Vue.observable

状态 data vs 属性 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
<template>
<div class="index-container">
{{name}}
{{info}}
</div>
</template>
<script>
export default {
data() {
this.name = name;
return {
info: {},
b:1
}
},

computed: {},

mounted() {},

methods: {
handleDataChange(){
// 不触发组件更新
this.name = 'vue' + Date.now();
this.b = 2;
},
// 不触发组件更新
handleAttributesChange(){
this.info.number = 1;
}
}
}
</script>

响应式更新

响应式更新

data return 内的数据,对象属性定义,并且在视图中展现更改时会触发组件的更新。

计算属性 computed 和 监听 watch

计算属性 computed

  • 减少模板中计算逻辑
  • 数据缓存
  • 依赖固定的数据类型(响应式数据)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// setter 和 getter
data(){
return{
firstName: 'Dell',
lastName: 'Lee'
}
},
computed:{
fullName:{
get:function(){
return this.firstName + " " + this.lastName
},
// 设置的时候触发
set:function(value){
var arr = value.split(" ")
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}

监听 watch

  • 更加灵活、通用
  • watch 中可以执行任何逻辑,如函数节流、Ajax 异步获取数据、甚至操作 DOM
1
2
3
4
5
6
7
8
9
10
11
12
13
watch:{
// 属性监听
"b.c":function(oldval,val){
}
// 深度监听
obj:{
handle:function(oldVal,val){

}
deep:true
}

}

生命周期的应用场景和函数式组件

Vue 生态

vue cli

Vue loader

vue router

vuex

vue ssr

PWA

Vue3

与Vue2.x相比,mount 50% 提升,内存占用小 120%
核心代码 + Composition API:13.5kb,最小11.75kb
所有 Runtime:22.5kb(Vue2 是 32kb)

Vue3.0新特性

按需加载 & 组合API
TS 支持,新增:Fragment、Teleport、Suspense
性能提示 1.3 ~ 2X

按需加载

基础: Virtual DOM , 响应式算法
非常用功能,按需加载,e.g.v-model,Transition

Compiler 原理篇


静态 Node 不再作更新处理(hoistStatic -> SSR 优化)
静态绑定的class, id 不再作更新处理
结合打包 Hint,进行更新分析(动态绑定)
事件监听器 Cache 缓存处理 (cacheHandlers)
hoistStatic 自动针对多静态节点进行优化,输出字符串
按需加载

setup
toRefs
生命周期

prettier-eslint

Vue2.x升级

90%以上代码可与vue2.0复用
Composition API 作为新增 API 不会影响旧的逻辑代码
Mixin不再推荐

新增功能

Fragment —— 不受根节点限制,渲染函数可接受 Array
Teleport —— 类似 Portal ,随用随取,e、g弹窗、Actions
Suspense —— 嵌套的异步依赖,e、g、async、setup()

更好的 Typescript 支持

更好的静态检查
更好的编程体验,类型推导,包括 Props
单模板文件进行类型检查 + 自动补全

更好的 Vue 生态

利用自定义渲染引擎、小程序 Runtime 跟进
NativeScript、WebGL(Vugel)、Three.js
自家生态:Vuex、VueRouter、Cli、DevTools

更新建议

小规模尝试,小项目尝试
Vue2.x 项目如果已经稳定,不推荐更新

Vite 开发

Vite 是一个 HTTP 服务器,特殊的地方:

  1. 可以在单文件中书写 ES6语法
  2. 支持热更新(请求的内容才会被打包/更新)
  3. Rollup 打包
1
2
3
4
yarn create vite-app <project-name>
cd <project-name>
yarn
yarn dev

@vue/cli-plugin-pwa

安装

1
vue add @vue/pwa
1
2
//  src/main.js
import './registerServiceWorker'

生成文件
src/registerServiceWorker.js
public/robots.txt
public/img/icons/

没生成以上文件卸了插件重新装。

该插件加入的 service worker 只会在生产环境下 (即只在运行 npm run build 或 yarn build 时) 开启。在开发环境下开启 service worker 并不推荐,因为它会导致之前的缓存资源被使用而未包含最新的本地改变。
取而代之的是,在开发环境下引入 noopServiceWorker.js。这个 service worker 文件会重置之前在相同主机和端口注册过的任何 service worker,有效地达到了 no-op 的目的。
如果你需要本地测试一个 service worker,构建应用并在构建目录运行一个简单的 HTTP 服务器。这里推荐使用浏览器隐私模式以避免浏览器缓存带来的问题。

我想要在 手机上测试下,但是pwa 只能在 localhost 或者 http 下才行,下载 Openssl 生成证书,但是也不行,浏览器不信任。

mkcert 也可以生成证书,pc端 浏览器是正常的,但是移动端浏览器是不信任的。最后花生壳解决。

pwa 入门与实践使用的是 HTTP Server

serve 静态文件服务

1
serve -s dist

vue.config.js 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwa: {
name: '豆瓣app',
themeColor: '#4DBA87',
msTileColor: '#000000',
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'black',
iconPaths: {
favicon32: './public/img/icons/favicon-32x32.png',
favicon16: './public/img/icons/favicon-16x16.png',
appleTouchIcon: './public/img/icons/apple-touch-icon-152x152.png',
maskIcon: './public/img/icons/safari-pinned-tab.svg',
msTileImage: './public/img/icons/msapplication-icon-144x144.png'
},
workboxPluginMode: 'InjectManifest', // 需要推送 Push 没得选
workboxOptions: {
swSrc: './public/service-worker.js', // 本地sw.js相对路径
importWorkboxFrom: 'disabled',
exclude: [/\.html$/, /\.css.map$/, /sw.js$/]
}
}

service-worker.js 代码

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
67
68
69
70
const CACHE_NAME = 'cache_v1' // 定义缓存名称

// 缓存资源
self.addEventListener('install', async event => {
// 开启一个 cache,得到 cache 对象
// cache 存储资源
const cache = await caches.open(CACHE_NAME)
await cache.addAll([
'/',
'/manifest.json',
'/img/7acb0a46f21fbe096b63cbbd84291b338744ebf8074a.6947db6b.jpg',
'/css',
'/js'
])
await self.skipWaiting() // 等待 spkipWaiting 结束,才进入到 active
})

// 删除旧资源
self.addEventListener('activate', async event => {
const keys = await caches.keys() // 清除旧的资源,获取所有资源的 key
keys.forEach(key => {
if (key !== CACHE_NAME) {
caches.delete(key)
}
})
await self.clients.claim() // 立即受控
})

// 拦截请求
self.addEventListener('fetch', async event => {
const req = event.request
const url = new URL(req.url)
// 只缓存同源内容
if (url.origin !== self.origin) {
return
}
if (req.url.includes('/api')) {
event.respondWith(networkFirst(req))
} else {
event.respondWith(cacheFirst(req))
}
})

// 网络优先
async function networkFirst(req) {
const cache = await caches.open(CACHE_NAME)
try {
// 先从网络读取最新的资源
const fresh = await fetch(req)
// 获取数据 更新缓存
cache.put(req, fresh.clone())
return fresh
} catch (e) {
// 去缓存中读取
const cached = await cache.match(req)
return cached
}
}

// cache 优先,一般适用于静态资源
async function cacheFirst(req) {
const cache = await caches.open(CACHE_NAME)
const cached = await cache.match(req)
if (cached) {
return cached
} else {
const fresh = await fetch(req)
return fresh
}
}

技巧

slots 新语法向 3.0 看齐

使用带有 “#” 的 新命名插槽缩写语法,在 Vue 2.60+ 中可用

1
2
3
4
5
6
7
8
9
10
11
12
<!-- BaseLayout.vue -->
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
1
2
3
4
5
6
7
8
9
10
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>

动态指令参数

@hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
// 一般组件内处理定时器的步骤
export default {
mounted(){
this.timer = setInterval(() => {},1000)
},
beforeDestroy(){
clearInterval(this.timer)
},
// 更好的做法
mounted() {
const timer = setInterval(() => { ... }, 1000);
//
this.$once('hook:beforeDestroy', () => clearInterval(timer);)
}
}
</script>

其他


开发工具(开发中有用的库或UI组件)

awesome-vue:vue相关的资源集合。

Vue3 (vue3js.cn):vue 相关的资源

sortablejs:可拖拽的列表。

lockr:用于localStorage的最小API包装器。

lodash:Lodash消除了处理数组,数字,对象,字符串等的麻烦,从而使JavaScript变得更容易。

moment:moment 是一个轻量级的JavaScript date库,用于解析、验证、操作和格式化日期。

dayjs:Moment.js 的 2kB 轻量化方案,拥有同样强大的 API。

NativeScript: JavaScript 访问本机 API。

cordova:使用HTML、CSS、JS进行移动 App 开发多平台共用一套代码。

vue-currency-filter:一个货币过滤器别看他的 star 少,但是他确实解决我的问题,他能帮助我加上千位符,并且加上小数点后两位还可以加货币符号,对于我这个菜鸟来说他太棒了。

Vue-Socket.io:Vuejs和Vuex的Socket.io实现

NProgress:纳米级进度条。带有逼真的le流动画,以说服您的用户正在发生某些事情!

vue-progress-path:进度条。

vue-awesome-swiper:轮播图。

vue-sticky-nav:用于移动端的吸顶导航组件。

v-charts:基于 Vue2.0 和 ECharts 封装的图表组件。但看到 star 数和好多开源项目都有使用,应该不会太差。

vuex-persistedstate:在页面重新加载之间坚持并重新补充您的Vuex状态。

RVerify.js:轻量级的图像旋转验证插件。

vue-monoplasty-slide-verify:一个Vue插件以滑动验证。

vue-drag-verify:这是一个Vue组件,可以滑动以解锁以进行登录或注册。这用于保护您的Web应用程序免受机器人攻击。

zx-verify:图形验证码组件。

vue-virtual-scroller:快速滚动,可浏览任意数量的数据。

v-hotkey:快捷键绑定到组件上

vue-awesome-swiper:vue 轮播组件

vue-core-image-upload:用于图像裁剪和上传

vue-quill-editor:富文本编辑器

Vue.D3.tree:Vue组件,用于基于D3.js布局显示树

liquor-tree:基于Vue.js的树组件

pdf-vue:vue.js pdf查看器

pdfjs-dist:PDF.js库的通用构建。

vue2-filters:vue2 中的一些过滤器

mint-ui:vue 的移动 UI 组件库。

vant:基于 Vue 构建的轻量级移动 UI 组件。

vuetify:Material 样式的 Vue UI 组件库。

ant-design-vue:基于蚂蚁设计和 Vue 的企业级 UI 组件。

DataV:Vue 大屏数据展示组件库。


开源项目

vue2-happyfri
vue-sell
vue2-elm
vue2-element-touzi-admin
vue-element-admin
d2-admin
vux
uniapp-admin

vue-cli4-config:vue-cli4配置vue.config.js持续更新

iview-admin:基于 iView 的 Vue 2.0 管理管理系统模板。

vue2-manage:基于 vue + element-ui 的后台管理系统。

vue2-demo:vue 基于 Genesis + TS + Vuex 实现的 SSR demo。

genesis:基于 Vue SSR 的微型前端、微型服务。

vue-manage-system:基于vue + element的后台管理系统解决方案。

bootstrap-vue:BootstrapVue 为 Vue 提供最全面的。

vue2-management-platform:vue2.0+ elementUI 后台管理平台

vuepress:简约的 Vue 供电静态站点生成器。好多人用它做博客。

vue-h5-template:基于 vue-cli4.0 + webpack 4 + vant ui + sass+ rem 适配方案+axios 封装,构建手机端模板脚手架。

ant-design-vue-pro:开箱即用的中台前端/设计解决方案。


源码解析

vue:vue源码逐行注释分析+40多m的vue源码程序流程图思维导图 (diff部分待后续更新)。

learnVue:Vue.js源码分析。

基于vue的可视化编辑器

Vue-Layout:基于UI组件的Vue可视化布局工具。
vue-design:使用Vue和Electron成为最佳的网站可视化构建器。
vue-page-designer:Vue组件,用于拖放来设计和构建移动网站。
X-Page-Editor-Vue:基于 Vue 的可视化布局编辑器插件。

可视化编辑

pl-drag-template:一个h5可视化编辑器项目
quark-h5:一个h5可视化编辑器项目
vue-json-schema-form:可视活动编辑器、h5编辑器。

webpack 构建 Vue 应用

运行项目可能出现的错误

1
2
3
4
5
6
7
output:{
publicPath: '/public/'
}

historyApiFallback: {
index: '/public/index.html'
}

升级 webpack 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
npm uninstall webpack webpack-dev-server 
npm i webpack webpack-dev-server -D // webpack 4.29.0 webpack-dev-server 3.1.14
npm install -g babel-upgrade // 升级使用
npx babel-upgrade --write // 写入升级配置删除重复配置
npm i
npm i webpack-cli -D

加入 mode 配置项
将 loader 全部卸载升级

npm i vue-loader@14.2.4 -D
npm i webpack@4.28.4 -D
npm i @babel/plugin-proposal-object-rest-spread -D

npm i vue-loader@15.6.2 -D

搭建 Vue 后台

vue-element-admin 手摸手系列

目录结构

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
├── build                      // 构建相关  
├── config // 配置相关
├── src // 源代码
│   ├── api // 所有请求
│   ├── assets // 主题 字体等静态资源
│   ├── components // 全局公用组件
│   ├── directive // 全局指令
│   ├── filtres // 全局 filter
│   ├── icons // 项目所有 svg icons
│   ├── lang // 国际化 language
│   ├── mock // 项目mock 模拟数据
│   ├── router // 路由
│   ├── store // 全局 store管理
│   ├── styles // 全局样式
│   ├── utils // 全局公用方法
│   ├── vendor // 公用vendor
│   ├── views // view
│   ├── App.vue // 入口页面
│   ├── main.js // 入口 加载组件 初始化等
│ └── permission.js // 权限管理
├── static // 第三方不打包资源
│   └── Tinymce // 富文本
├── .babelrc // babel-loader 配置
├── eslintrc.js // eslint 配置项
├── .gitignore // git 忽略项
├── favicon.ico // favicon图标
├── index.html // html模板
└── package.json // package.json

views 和 api
两个模块一一对应,方便维护。公共的 api 单独放置。

components
放置全局公用组件,页面级组件建议放置各自的 views 文件下。

store
不要为了用 vuex 而用 vuex!

alias
为文件夹添加别名

ESLint

ESLint 配置
eslint规则

封装 axios

多环境

前后端交互

跨域问题

cors
dev 环境可以通过 webpack-dev-server的proxy来解决,nginx反代理

前后端的交互问题

swagger 是一个REST APIs文档生成工具

前端自行 mock

mock server
mockjs + rap
easy-mock

字体图标

iconfont

router-view

优化

需要利用 pm2 进行 nodejs 生命周期的管理
webpack dll
AggressiveSplittingPlugin

权限验证

  • 登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个 token,拿到 token 之后(我会将这个 token 存贮到cookie中,保证刷新页面后能记住用户登录状态),前端会根据 token 再去拉取一个 user_info 的接口来获取用户的详细信息(如用户权限,用户名等等信息)。
  • 权限验证:通过 token 获取用户对应的 role,动态根据用户的 role 算出其对应有权限的路由,通过 router.addRoutes 动态挂载这些路由。

上述所有的数据和操作都是通过vuex全局管理控制的。(补充说明:刷新页面后 vuex的内容也会丢失,所以需要重复上述的那些操作)接下来,我们一起手摸手一步一步实现这个系统。

后端返路由操作

1
2
3
4
5
6
7
8
9
10
const map={
login:require('login/index').default // 同步的方式
login:()=>import('login/index') // 异步的方式
}
//你存在服务端的map类似于
const serviceMap=[
{ path: '/login', component: 'login', hidden: true }
]
//之后遍历这个map,动态生成asyncRoutes
并将 component 替换为 map[component]

Vue 思考题

  • 子组件为何不可以修改父组件传递的 Prop,如果修改了,Vue 是如何监控到属性的修改并给出警告的?

  • this.$emit 的返回值是什么?

  • 相同名称的插槽是合并还是替换zi

  • 为什么不能用 index 作为 key ?

  • 数组有哪些方法支持响应式更新,如不支持如何处理,底层原理如何实现的?

MVVM 模式

MVP 模式

Model 模型层 View(视图层) Presenter(控制器)

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
<!-- jQ 演示 MVP 设计模式 -->
<div>
<input id="input" type="text"/>
<button id="btn">提交</button>
<ul id="list">

</ul>
</div>
<script>
function Page(){

}
$.extend(page.prototype,{
init: function(){
this.bindEvents()
},
bindEvents: function(){
var btn = $("#btn");
btn.on('click',$.proxy(this.handleBtnClick,this))
},
handleBtnClick: function(){
var inputElem = $("#input");
var inputValue = inputElem.val();
var ulElem = $("#ul");
ulElem.append('<li>'+inputValue+'</li>')
inputElem.val("")
}
})
var page = new Page();
page.init()
</script>

MVVM 模式

Model 模型层 View(视图层) VM(控制器)


相关资料

[视频]vue2.5入门

[视频]3小时速成 Vue2.x 核心技术

[视频]动力学院vue 教程

[视频]vue.js 教程

[视频]vue-cli全集

[视频]Vue+Webpack打造todo应用

[视频]【黑马程序员】VUE进阶-从0到1搭建UI组件库(1-3)

[视频]Vue2.5-2.6-3.0 开发去哪儿网App 从零基础入门到实战项目开发
[视频]从0开始搭建大型Vue项目 p12

[视频]vue后台管理系统
[视频]通俗易懂的Vue +Elment-UI后台管理系统
[视频]Vue之父尤雨溪深度解读Vue3.0开发思路附 在线视频

Prop
.sync 修饰符
sync修饰符示例

cordova+vue 项目打包成Android(apk)应用

关于cordova+vue打包apk文件无法访问数据接口

CORDOVA+VUE打包APK文件无法访问数据接口

谈谈 Vue 开发过程中用到的插件
Vue style里面使用@import引入外部css, 作用域是全局的解决方案
借助axios的拦截器实现Vue.js中登陆状态校验的思路
不吹不黑也不撕,我们就简简单单谈谈Vue
为什么Vue中的slot不能应用v-show指令
困扰99%前端程序员的Vue问题,全在这了(含代码)
使用vue开发移动端管理后台
Vue 项目棘手问题的解决方案
Vue应用部署到服务器的正确方式
Vue的全局组件
Error: Node Sass version 5.0.0 is incompatible with ^4.0.0
错误:gyp ERR! find Python
解决:npm install报错gyp ERR&! stack Error: Can‘t find Python executable python
Sass尚不支持您当前的环境:Windows 64位
vue-cli中 如何引入全局的js

如何在自定义的js文件中获取vuex中state实时变化的值

vue中如何利用js如何检测网络状况,监听网络状态

pdf.js实战,含水印、电子签章解决方案

45个实用Vue.js 开源项目

关闭ESlint 语法检测配置方法

vue中computed计算属性传入参数

vue用命令直接修复ESLint

vue中实现回到顶部功能

vue使用Moment插件格式化时间

vue-cli4最新版中怎么引入jQuery

vue引入zTree入门


关于 element ui

vue-element-admin 如何设置三级路由,只显示一个子菜单

element el-table resetfields() 不生效

vue项目elementUI预览图片

elementUI table组件会出现空白部分,怎么解决

Element-UI的点击按钮实现图片预览功能

Vue项目打包部署总结

分享8个非常实用的Vue自定义指令

Vuex Module

手摸手,带你用vue撸后台 系列一(基础篇)

vue 长按与点击事件
vue移动端在线签名
vue在移动端实现电子签名手写板
使用vue实现一个电子签名组件

模仿element-ui封装vue组件库(一)
模仿element-ui封装vue组件库(二)
模仿element-ui封装vue组件库(三)

natapp:内网穿透工具

花生壳:内网穿透工具

Windows安装使用Openssl

内网穿透的几种方式

使用utools一键实现内网穿透_二赛君-CSDN博客

ngrok:ngrok 的使用教程

FastTunnel

frp

openssl基本原理 + 生成证书 + 使用实例

mkcert:生成证书。

如何在本地自建 ssl 证书给 localhost 使用并通过 Chrome 和 Safari 浏览器的验证?

@vue/cli-plugin-pwa

基于vue-cli3创建的项目引入PWA(一)

基于vue-cli3创建的项目引入PWA(二)

技巧

10个简单的技巧让你的 vue.js 代码更优雅🍊 (juejin.cn)

浅析 vue-router 源码和动态路由权限分配
vue3源码解析和最佳实践

从零开始搭建一个简单的基于webpack的vue开发环境
让Jenkins自动布署你的Vue项目

Vue源码系列-Vue中文社区

[代码]使用vue实现一个电子签名组件 代码
包教包会,手摸手使用Vue3重构vue2项目(实用)
egg+vue+mongodb实践开发在线文档管理平台——水墨文档

Weex demo 网易严选
Weex Ui
Weex实战开发
Weex 入门这一篇就够了

最全Vue知识点(基础到进阶,覆盖vue3)
前端黑科技!结合 Vue 让首页秒开

一枚程序媛,献上开箱即用的 vue+vant 方案
element-ui的upload组件的clearFiles方法的调用
移动端吸顶导航组件的实现
Vue UI库
ElementUI 组件库之外,供我们选择的 Vue 组件库还有很多!
Vue3开源组件库,今天“它们”来了
Vue 前端可视化活动编辑器__Vue.js
Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5

Vue中组件之间8种通信方式,值得收藏
vue组件间通信六种方式(完整版)
数据动态过滤技巧在 Vue 项目中的实战
9个适用于React,Vue,Angular的Bootstrap UI组件库
Vue 项目性能优化 — 实践指南(网上最全 / 详细)
vue全家桶开发的一些小技巧和注意事项
vue / react的UI库都在用的几个DOM API
揭秘vue/react组件库中5个”作者不造的轮子”
vue踩坑总结 & 优化点
NutUI
Vue 项目性能优化 — 实践指南(网上最全 / 详细)
尤雨溪自述:打造Vue 3.0背后的故事
搭建一个vue-cli4+webpack移动端框架(开箱即用)

Vue 中多个组件可以共享数据和方法 Mixin 的用法
vue全家桶开发的一些小技巧和注意事项
Vue这些修饰符帮我节省20%的开发时间
10个Vue开发技巧助力成为更好的工程师(二)

讲道理:为什么 Vue 中不要用 index 作为 key?(diff 算法详解)
实现微前端,你需要了解的 Vue Genesis 渲染器
封装 axios 拦截器实现用户无感刷新 access_token
总结我对Vue项目上线做的一些基本优化
10个简单的技巧让你的 vue.js 代码更优雅
Vue3.0前置,还不知道这些就来不及啦
Vue+Node+高德地图+Echart做一款出行可视化全栈webapp
Vue最全知识点,面试必备(基础到进阶,覆盖vue3.0,持续更新整理,欢迎补充讨论)
10个Vue开发技巧助力成为更好的工程师
vue+router+axios 实现后台管理系统登录拦截(权限控制)

【Vue3官方教程】🎄万字笔记 | 同步导学视频

总结我对Vue项目团队开发的一些基本配置封装分享

前端开发 直播课&公开课

腾讯课堂

慕课网

网易云课堂

一节课让你面试无惧vue响应式原理

  • Vue 2 的数据响应式原理
  • Vue 3 的数据响应式原理
  • Diff 算法和 virtual dom

什么是 defineProperty

defineProperty 其实是定义对象的属性
defineProperty 其实并不是核心的为一个对象做数据双向绑定,而是去给对象做属性标签,只不过属性里的 get 和 set 实现了响应式。

属性名 Second Header
value undefined
get undefined
set undefined
writable false
enumerable false
configurable false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
a : 1,
b : 2
}

Object.defineProperty(obj,'a',{
writable : false,
enumerable : false,
configurable : false,
})
// getOwnPropertyDescriptor 返回指定对象上一个自有属性对应的属性描述符
console.log(Object.getOwnPropertyDescriptor(obj,'a'));

// output
// {value: 1, writable: false, enumerable: false, configurable: false}

面试加分技术点-ssr怎么做

什么是 ssr

传统的 vue 纯浏览器渲染

传统的 vue 纯浏览器渲染

这样渲染造成的影响
1、不利于SEO;搜索引擎优化
2、首屏问题;所有文件都要一次性加载完成,通过按需加载一定程度上缓解
3、性能问题;所有渲染都交给浏览器,造成浏览器性能下降。

ssr
ssr

1
2
3
4
5
6
7
8
9
10
11
12
const vue = require("vue");
const renderer = require('vue-server-renderer').createRenderer();
const app = new vue({
template: `<div>Hellow World<span>{{num}}</span></div>`,
data: {
num: 123
}
})
// 将 vue 变成字符串返回
renderer.renderToString(app).then(html => {
console.log(html);
})

node

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
const vue = require('vue');
const server = require('express')();
const renderer = require('vue-server-renderer').createRenderer();
const fs = require('fs');

function createApp(url){
if(url == ''{
url = '/index'
})
}

// 读取对应的数据 数据存在 json 文件中
var json = fs.readFileSync(`json${url}.json`,'utf-8');
var tempalte = fs.readFileSync(`tempalte${url}.html`,'utf-8');

return new vue({
tempalte: tempalte,
data: JSON.parse('json').data
})

server.get("*",(req,res) => {
if(req.url != '/favicon.ico'){
const app = createApp(req.url);
renderer.renderToString(app).then(html => {
res.end(html);
})
}
})

server.listen(7070)

特性

每一次访问必须新建一个 vue 实例
只会触发组件的 beforeCreate 和 creatd 钩子,所以需要客户端 js。

自己动手搭建一个 ssr + vue 项目

ssr 需要哪些东西

ssr

vue ssr 框架 nuxt

分别打包好客户端和服务端
加 nginx 代理

vue高级实战技巧-骚操作和最佳实践

优化三字经:快(执行快)、小(文件小)、省(减少 http请求)。
缓存 - 是所有优化方案里,最常用,最有效的,可以节省 http 请求,可以从内存里最快读取数据

vue3 优化
1、优化 diff 对比
2、define 改为 proxy
3、100kb 到 30kb 函数编程

api 数据缓存
资源缓存是浏览器自动缓存

请求缓存思路

请求缓存思路

缓存架构

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
//  main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

// 1、缓存区域唯一、单一,参考单例模式。
// 2、缓存数据不能直接操作
// 3、缓存的问题 存在内存中 更新问题解决
// v8 标准 64 1.4g 32 0.7g

if(!window.mycache){
window.mycache = ( function (){
var cache = {}
var cacheArr = []
return {
get:function(api){
return new Promise((resolve,reject) => {
if(cache[api]){
resolve(cache[api])
}else{
this.set(api).then(()=>{
if(cacheArr.length == 1){
var _api = cacheArr.shift();
this.remove(_api);
}
cache[api] = res;
cacheArr.push(api);
resolve(res);
})
}
})
},
set:function(api){
return axios.get(api)
},
remove:function(){

}
}
})()
}

new Vue({
el: '#app',
router,
render: h => h(App)
})

vue 插件

Vue.use
原理:判断传入的东西有没有 install,有则执行 install,没有 install 并且传入的是一个方法的话执行传入的东西

Vue.mixin

插件
提供逻辑复用
注入自定义操作

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
//  main.js
var a = {
install:function(vue){
// 生命周期混入
Vue.mixin({
beforeCreate:function(){
// this 指向当前组件
// 拿到当前组件 export default 的对象
// 异步加载 vuex
if(this.$options.isVuex){
var name = this.$options.name
import('./store/module'+name).then(res =>{
this.$store.registModule(this.$options.name,res.default)
})
}
},
data:function(){
return {
b: 123
}
},
methods: {
a:function(){

}
}
})
}
}
Vue.use(a);

vue 底层 api 运用

  • Vue.util.defineReative:把一个外部数据的改变能够触发 vue 的视图更新
    vue-router
    原理 hash 值改变 > 触发 vue-router 对象的 current 变量改变 > 触发视图更新

  • Vue.extend

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//  main.js
// 返回一个 vue 的构造
Vue.extend({})
// index.vue

// 调用 弹窗组件
<button @click="show('hello',$ref.cover)"></button>
<div ref = "cover">
</div>

// 组件
import pop from './components/pop.vue'
// 弹窗
Vue.mixin({
methods: {
show:function(mes,el){
const popContructor = Vue.extend(pop);
const vm = new popContructor();
vm.$data.mes = mes;
vm.$mount (el);
}
}
})

单元测试 > vue 某个组件的方法执行结果对不对,渲染结果 > 需要拿到 vue 的实例

extend:帮助我们跟 vue 没有关系的系统里能够拿到 vue 的组件实例,并且能够很好的模拟真实挂载状态

  • 利用 render 渲染

render 代替 template:提供用 js 描述 template复杂逻辑

1
2
3
render:function(h){
h("h" + this.level)
}

相关资料
深入响应式原理
[视频]网易高级前端开发系列直播课②
[视频]网易高级前端开发系列直播课④
[视频]网易高级前端开发系列直播课⑤
[视频]网易高级前端开发系列直播课⑥
[视频]网易高级前端开发系列直播课⑦
[视频]网易高级前端开发系列直播课⑧
[视频]网易高级前端开发系列直播课⑨
[视频]【网易】高级前端4天试学营9.21
[视频]【网易】高级前端4天试学营3
Vue服务器渲染器
Vue.js 服务器端渲染指南
前端权威0基础入门 + Web百大项目案例

Vue.js 项目实战

Vue 开发入门

Vue 是一个专注于构建 Web 用户界面JavaScript 库。

1.1 为什么需要另外一个前端框架

Vue 易用、灵活、速度快、还提供了许多功能和可选工具,这使得开发者能够快速地构建一个现代 web 应用。Vue 的作者尤雨溪将其称为渐进式框架

  • Vue 遵循渐进增量的设计原则,其核心库专注于用户界面,使得现有的项目可以方便地集成使用 Vue。
  • Vue 既可以构建出很小的原型,又可以构建出复杂的大型 web 应用。
  • Vue 非常容易上手 —— 初学者能轻松掌握 vue,而已经熟悉 Vue 的开发者则可以在实际项目中快速发挥出它的作用。

Vue 整体上遵循 MVVM(Model-View-ViewModel,模型 - 视图 -视图模型)架构,也就是说 View(用户界面或视图)和 Model(数据)是独立的,ViewModel(Vue)是 View 和 Model 交互的桥梁。Vue 对 View 和 Model 之间的更新操作做了自动化处理,并且已经为开发者进行了优化。

Vue 还吸收了其他类似框架( 如 React、Angular、Polymer)的精华。

Vue 核心功能概述

  • 一个响应式的数据系统,能通过轻量级的虚拟 DOM 引擎和最少的优化工作来自动更新用户界面。
  • 灵活的视图声明,包括优雅友好的 HTML 模板、JSX(在 JavaScript 中编写 HTML 的技术)以及 hyperscript 渲染函数(完全使用 JavaScript )。
  • 由可维护、可复用组件组成的组件化用户界面。
  • 官方的组件库提供了路由、状态管理、脚手架以及更多高级功能,使 Vue 成为了一个灵活且功能完善的前端框架。

Weex、NativeScript

1.1.2 兼容性需求

Vue 不支持 Internet Explorer 8 以下版本,因为 Vue 使用了 JavaScript 中相对较新的特性,比如 Object.defineProperty,
而它们在老版本的浏览器中是无法 polyfill 的。

编译器 Babel ,它编译过的代码可以很好地运行在老版本浏览器中。

1.3 创建一个应用

整个库都是基于 Vue 实例的,而实例是 View 和 Model(数据)交互的桥梁。因此需要创建一个新的 Vue 实例来启动应用。

1
2
3
4
5
6
7
8
9
10
11
//  创建 Vue 实例
var app = new Vue({
// 根 DOM 元素的 CSS 选择器
el: '#root',
// 一些数据
data(){
return {
message: 'Hello Vue.js'
}
}
})

使用关键字 new 调用 Vue 构造器创建了一个新的实例。Vue 构造器有一个参数 —— option 对象。该参数可以携带多个属性(称为选项)。

通过 el 选项,我们使用 CSS 选择器告知 Vue 将实例添加(挂载)到 Web 页面的哪个 DOM 元素中。
也可以 Vue 实例的 $mount 的方法代替el选项:
app.$mount(‘#root’)。

1
2
3
4
5
6
7
8
9
var app = new Vue({
data(){
return {
message: 'Hello Vue.js'
}
}
})
// 添加 Vue 实例到页面中
app.$mount("#root");

Vue 实例的大多数特殊方法和属性都是以美元符号($)开头的。

在单个 Web 页面中,开发者可以添加任意多个 Vue 应用。只需要为每个应用创建出
新的 Vue 实例并挂载到不同的 DOM 元素即可。当想要将 Vue 集成到已有的项目中时,这非常方便。

Vue 开发者工具

Vue 有一个官方调试工具,在 Chrome 中以扩展的方式呈现,名为 Vue.js devtools。通过该工具可以看到应用的运行情况,这有助于调试代码
。可以在 Chrome 网上应用商品下载;如果使用 Firefox,则可以到 Firefox 附加组件 下载。
使用 Chrome 版本的话,还需要进行额外的设置。在扩展设置中,启用 Allow access to file URLs 选项。这样调试工具就能在从本地磁盘打开的 Web 页面上检测 Vue 了。

可以将 devtools 选项卡拖放到喜欢的位置。建议将其放在靠前的位置,因为当 Vue 不处于开发模式或没有运行时,该选项卡在页面中是隐藏起来的

可以通过 name 选项修改 Vue 实例的名字:

1
2
3
4
var app = new Vue({
name : 'MyApp',
// ...
})

当一个页面中有多个 Vue 实例时,这有助于直观地在开发者工具中找到具体的某个实例。

1.4 借助模板实现 DOM 的动态性

模板是描述 View 最简单的方法,只需要少量额外的语法就能轻松实现 DOM 的动态更新。

1.4.1 文本显示

文本插值用于在 Web 页面中显示动态的文本。文本插值的语法是在双花括号内包含单个任意类型的 JavaScript 表达式。当 Vue 处理模板时,该 JavaScript 表达式的结果将会替换掉双花括号标签。

1
2
3
<div id="root">
{{ message }}
</div>

DOM 和 数据连通了

1
app.message = 'Awesome!'

数据绑定,每当数据有改变时, Vue 都能够自动更新 DOM,Vue 框架中包含一个非常强大且高效的响应系统,能对所有的数据进行跟踪,并且能在数据发生改变时按需自动更新 View。

1.4.2 利用指令添加基本的交互

Vue 中所有的指令命名都是带 V- 前缀的,并遵循短横线分隔式(kebab-case)语法。这个意味着要用短横线将单词分开。HTML 属性是不区分大小写的。

1
2
3
4
5
<div id="root">
<p>{{message}}}</p>
<!-- 添加一个文本输入框 -->
<input v-model = "message">
</div>

双向数据绑定 v-model

项目 1:Markdown 笔记

v-model 指令不限于文本输入使用。它同样可以用于其他元素,例如勾选框、单选按钮,甚至自定义组件。

2.1.3 预览面板

计算属性 通过它可以定义一个新的属性,而该属性可以结合任意多个属性,并做相关转换操作。

  • 计算属性的值基于它的依赖进行缓存,因此如果没有必要是不会重新运行函数的,从而有效防止无用计算;
  • 当函数中用到的某个属性发生了改变,计算属性的值也会根据需要自动更新;
  • 计算属性可以如其他普通属性一起使用(可以在其他计算属性中使用计算属性);
  • 计算属性只有真正用于应用中时,才会进行计算操作。
在应用内,不建议使用 v-html 指令对用户提供的内容做 HTML 插值。这是因为用户可能会在标签中编写不怀好意、会被执行的 JavaScript 代码。当然对普通文本做插值是安全的,因为 HTML 不会被执行。

元素中的任意内容将被 v-html 指令的值替代。可以利用这一点来放置占位符内容。

对于文本插值,v-text 是一个与 v-html 等效的指令。它的行为与 v-html 类似,只不过会对 HTML 标签做转译处理,形同典型的文本插值。

2.1.4 保存笔记

localStorage 保存笔记

侦听改变

监听器 watch 选项是一个字典,把被侦听属性的名字作为键,把侦听选项对象作为值。这个对象必须要一个 handler 属性,该属性可以是一个函数,也可以是一个方法的名字。这个处理函数将接收两个参数:被侦听属性的新值和旧值。

1
2
3
4
5
6
7
8
9
10
new Vue({
// 侦听 content 数据属性
watch: {
content: {
handler(val,oldVal){
console.log('new note:', val , 'old note' , oldVal)
}
}
}
})

还有另外两个选项可以和 handler 一起使用。

  • deep 是一个布尔类型,告诉 Vue 以递归的方式侦听嵌套内部值的变化。
  • immediate 也是一个布尔类型,会立即触发调用处理函数,而不用等到属性第一次变化时才调用。
这两个选项的默认值都是 false,所以不需要使用的时候,可以完全忽略它们。 当不需要其他选项(例如 deep 或 immediate)时,这是侦听器中最常用的语法。
1
2
3
4
watch: {
content(val,oldVal){
}
}

保存笔记

1
localStorage.setItem('content',val)

复用方法
良好的编程准则之一:不要重复自己(DRY),也称为一次仅且一次。开发者应该遵守这个准则。因此可以把一些逻辑写在
可复用的函数里面:methods。

1
2
3
4
5
6
7
8
9
10
11
12
new Vue({
watch:{
content: 'saveNote'
},
methods:{
saveNote(val){
console.log('saving note:',this.content);
localStorage.setItem('content',thie.content)
}
}
})

访问 Vue 实例

在 methods 内部,可以通过 this 关键字访问 Vue 实例,还可以访问 Vue 实例的其他属性或特殊函数。

基本上可以在任意函数(方法、处理函数或其他钩子)其中使用 this 关键字访问 Vue 实例。 生命周期钩子

生命周期钩子

每个 Vue 实例都严格遵守一个生命周期,包括多个环节:创建、挂载到页面、更新,最终被销毁。例如,在创建实例阶段,Vue 会将实例数据变成响应式数据。

钩子是一组特殊的函数,会在某个时间点被自动调用。这就允许我们自定义框架的逻辑。例如在创建 Vue 实例时调用一个方法。

在每个环节之中或之前,有很多钩子可以用于执行逻辑。

  • beforeCreate:在 Vue 实例被创建时(例如使用 new Vue({ }))、完成其他事项之前调用。
  • created:在实例准备就绪之后调用。注意,此时实例还没有挂载到 DOM 中。
  • beforeMount:在挂载(添加)实例到 web 页面之前调用。
  • mounted:在实例被挂载到页面并且 DOM 可见时调用。
  • beforeUpdate:当实例需要更新时(一般来说,是当某个数据或计算属性发生改变时)调用。
  • updated:在把数据变化应用到模板之后调用。注意此时 DOM 可能还没有更新。
  • beforeDestroy:在实例销毁之前调用。
  • destroyed:在实例完全销毁之后调用。
在 JavaScript 中,如果值为 false、0、空字符传、null、undefined 或 NaN(不是一个数),则它就是假值。在浏览器的本地存储数据中,如果对应的键不存在, localStorage.getItem() 方法会返回 null。

在数据中直接初始化

1
2
3
4
5
6
7
new Vue({
data(){
return{
content:localStorage.getItem('content') || 'You can write in **markdown**'
}
}
})

2.2 多条笔记

2.2.1 笔记列表

添加新建笔记的方法
每一条笔记都是具体如下数据的对象。

  • id :笔记的唯一标识符。
  • title :笔记的标题,用来显示在笔记列表中。
  • content : 笔记的 Markdown 格式内容。
  • created : 笔记创建的日期。
  • favorite : 这是一个布尔型,用于表示是否收藏了笔记,已收藏的笔记显示在笔记列表的顶部。

选择当前时间(也就是从 1970 年 1 月 1 日 00:00:00 UTC 开始经过的毫秒数)作为区分笔记的唯一标识符,这是一种不错的方式。

用 v-on 实现按钮的单击事件

1
<button v-on:click="addNote"></button>

绑定事件 v-on: 简写 @

1
<button @:click="addNote"></button>

用 v-bind 绑定属性

1
<button v-bind:title="notes.length + ' note(s) already'">

属性绑定 v-bind: 简写 :

1
<button :title="notes.length + ' note(s) already'">
当需要更新属性值的时候,用 v-bind 指令绑定的 JavaScript 表达式会自动重新运算。

用 v-for 显示列表

1
2
3
<div class="notes">
<div class="note" v-for="note of notes">{{note.title}}</div>
</div>

2.2.2 选择一条笔记

动态 CSS 类

1
<div :class= "{ one: note === selectedNote }"></div>
可以把静态与动态的 class 属性结合起来。建议将非动态的类放到静态的属性中,Vue 会对静态值做优化处理。

条件指令 v-if
v-else-if
v-else

标签不会出现在 DOM 中。用于对实际的元素进行重新组合。

侦听器默认值侦听目标对象的直接变化:赋值、数组添加、删除、移动。
例如:

1
2
3
4
5
6
7
8
9
//  赋值
this.selectedId = 'abcd'

// 在数组中添加或删除某项
this.selectedId.push({...})
this.selectedId.splice(index,1);

// 数组排序
this.notes.sore(...)

操作不会触发侦听器

1
2
3
4
5
6
//  给某个属性或者嵌套对象赋值
this.myObject.someAttribute = 'abcd';
this.myObject.nestedObject.otherAttribute = 42

// 修改数组中某项的内容
this.notes[0].content = 'new content'

这种情况下,需要在侦听器上添加 deep 选项

1
2
3
4
5
6
7
watch: {
notes: {
handle : 'saveNotes',
// 需要使用这个选项来监听数组中每个笔记属性的变化
deep: true
}
}

按照收藏、时间排序

1
2
3
4
5
6
7
computed: {
sortedNotes(){
return this.notes.slice()
.sort((a,b) => a.created - b.created)
.sort((a,b) => (a.favorite === b.favorite) ? 0 : a.favorite ? -1 : 1)
}
}
由于 sort 方法会直接修改源数组,这里使用 slice 方法创建新的副本。这样可以防止 notes 监听器。

创建日期过滤器

(1)引入 momentjs

1
<script src="https://unpkg.com/moment"></script>

(2) 使用 Vue.filter 全局方法(不在 Vue 实例的创建代码中,比如位于文件开头)注册过滤器

1
Vue.filter('date',time=> mement(time).format('DD/MM/YY, HH:mm'))
1
<span class="value">{{ selectedNote.created | date }}</span>

文本统计

1
2
3
4
5
6
7
8
9
10
11
12
//  计算换行符的格式 
str.split(/\r\.|\r|\n/).length
// 将换行符转换为空格
str.replace(/\n/g, '');
// 排除开头和结尾的空格
str.replace(/(^\s*)|(\s*$)/gi, '')
// 将多个重复空格转换为一个
str.replace(/\s\s+/gi, ' ');
// 返回空格数量
str.split(' ').length;
// 字符数量
str.split('').length

项目 2:城堡决斗游戏

在 Vue 实例构造器之后,添加一个事件监听器到 window 对象中,监听浏览器窗口大小的变化。

1
2
3
4
//  窗口大小变化的处理
window.addEventListener('resize', () => {
state.wordRatio = getWorldRatio();
}

3.3.3 万能的组件

组件是构建应用的基础模块,是 Vue 应用的核心概念。组件是视图的一个个小部分,因此相对来说应用比较小、可复用,并且尽可能地自给自足。采用组件构建应用有助于应用的维护和升级,特别是当应用规模变大之后。实际上,这已经成为了高效、可控地开发大型 Web 应用的标准方法。

组件

可以使用全局函数 Vue.component() 来注册组件。该函数接受两个参数:一个是注册组件的名称,另一个则是组件的定义对象本身,它与 Vue 实例使用相同的选项。

1
2
3
Vue.component('top-bar',{
template:`<div class='top-bar'>Top bar</div>`
})

使用 prop 进行父组件到子组件的通信

利用 props 选型可以将 prop 添加到组件中。

1
2
3
4
5
6
7
8
Vue.component('top-bar',{
props: ['players','currentPlayerIndex','turn'],
created(){
console.log(this.players);
}
})

<top-bar :turn="turn" :current-play-index="currentPlayIndex" :players="players">
注意:由于 HTML 是不区分大小写的,建议对 prop 的名字使用短航线命名方法(kebab-case),而在 JavaScript 代码中使用驼峰式命名方法(camel-case)。

模板中的 prop

通过 players prop 显示玩家姓名。

1
2
3
4
5
6
template:`
<div class="top-bar">
<div class="player p0">{{ players[0].name}}</div>
<div class="player p1">{{ players[1].name}}</div>
</div>
`

在组件上监听原生事件
为监听到组件的 click 事件,需要对 v-on 指令使用 .native 修饰符。

1
<card :def="testCard" @click.navtive="handlePlay">

使用自定义事件进行子组件到父组件的通信
在组件内部,使用 $emit 这个特殊方法触发的事件可以被父组件捕获到。该方法接收一个固定的参数,即事件类型:

1
this.$emit('play','orange',42)

可以使用名为 $on 的特殊方法监听自定义事件:

1
2
3
4
5
6
7
8
9
10
11
this.$on('play', () => {
console.log('Caught a play event!');
})

<card v-on:play="handlePlay"/>>
// 简写:
<card @play="handlePlay"/>

handlePlay(color,number){
console.log('handle play event','color = ' , color ,'number=',number)
}
Vue 的自定义事件与浏览器事件系统是完全分开的。方法 $on 和 $emit 并不是 addEventListener 和 dispatchEvent 的别名。这也解释了为什么在组建中需要使用 .native 修饰符来监听浏览器事件(如 click)。 大多数情况下,最好使用自定义事件完成组件之间的通信。

hand 组件的动画过度效果

动画效果:CSS 过渡结合 <transition> 组件。当添加或移除元素时,使用 v-if 或 v-show 指令来帮助实现 CSS 过渡。

1
2
3
4
5
//  应用状态集合
var state = {
// 用户界面
activeOverlay: null
}
1
2
3
<transition>
<hand v-if="!activeOverlay"/>
</transition>

<transition> 特殊组件不会显示在 DOM 中。

当元素被添加到 DOM 时(进入阶段),<transition> 组件会自动将下列 CSS 类应用到元素中。

v-enter-active: 当进入过渡状态被激活时,会应用该类。在元素插入 DOM 之前,添加该类到元素中,并在动画结束时移除它。应该在这类中添加一些 transition CSS 属性并定义其过渡时长。

v-enter: 元素进入过渡的开始状态。在元素插入 DOM 之前,添加该类到元素中,并在元素被插入的下一帧移除。例如,你可以在这个类中设置透明为 0。

v-enter-to: 元素进入过渡的结束状态。在元素插入 DOM 后的下一帧添加,同时 v-enter 被移除。当动画完成后,v-enter-to 会被移除。

当元素从 DOM 中移除时(离开阶段),<transition> 组件会自动将下列 CSS 类应用到元素中。

v-leave-active:当离开过渡状态被激活时,会应用该类。当离开过渡触发时,添加该类到元素中,并在从 DOM 中移除元素时移除它。应该在这个类中添加一些 transition CSS 属性并定义其过渡时长。

v-leave:元素被移除时的开始状态。当离开过渡触发时,添加该类到元素中,并在下一帧移除。

v-leave-to:元素离开过渡的结束状态。在离开过渡触发后的下一帧添加,同时 v-leave 被移除。当从 DOM 中移除元素时,该类也会被移除。

在离开阶段,并不会立即从 DOM 中移除元素。当过渡结束后,才会将其移除,这样用户可以看到动画效果。

进入 v-enter-active
v-enter v-enter-to
透明度0 -> 透明度1

离开 v-leave-active
v-leave v-leave-to
透明度1 -> 透明度0

<transition> 组件会自动检测应用在元素上的 CSS 过渡效果的持续时间。

复用动画

1
2
3
<transition name="fade">
<hand v-if="!activeOverlay" :cards="testHand">
</transition>
1
2
3
4
5
6
7
8
9
10
/* transition.css */
.fade-enter-active,
.fade-leave-acteve {
transition: opacity 1s;
}

.fade-enter,
.fade-leave-to {
opacity: 0;
}

现在只需要通过 <transition name="fade">标签就可以在任意元素上复用这个动画了。

贝塞尔曲线缓动函数,可以使得动画更加平滑。

1
2
3
4
5
6
7
8
9
10
.hand-enter-active .wrapper,
.hand-leave-active .wrapper {
transition: transform .8s cubic-bezier(.08,.74,.34,1);
transform-origin: bottom center;
}

.hand-enter .wrapper,
.hand-leave-to .wrapper {
transition: rotate(90deg);
}

为元素列表添加动画效果,需要使用另外一个特殊的组件 <transition-group>。当元素被添加、移除和移动时,该组件将对它的子元素做出动画效果。 跟 <transition> 元素不同的是,<transition-group>默认情况下会作为<span> 元素出现在 DOM 中。可以使用 tap prop 修改这个 HTML 元素。

1
2
3
<transition-group tag="ul">
<li v-for="item of items" />
</transition-group>

<transition-group> 的子元素必须由唯一的 key 做标识。

1
2
3
4
// 指定过渡效果名称为 card 
<transition-group name="card" tag="div" class="cards">
<card v-for="card of cards">
</transition-grou

特殊的 key 属性
当 Vue 更新存在于 v-for 循环中的 DOM 元素列表时,会尽量最小化应用于 DOM 的操作,例如添加和移除元素。大多数情况下,这是更新 DOM 的一种非常高效的方法,并且对性能的提升也有帮助。
为了做到这一点,Vue 会尽可能地复用元素,并仅对 DOM 中需修改的地方进行最小范围的修改,以达到理想的结果。这意味着重复的元素会被打包到一起,不会在添加或移除列表中的项时移动它们。不过,这也意味着对其应用过渡不会有动画效果。

key 属性为元素指定唯一标识符。

使用插槽分发内容

1
2
3
4
5
<div class="overlay" @click="handleClick">
<div class="content">
<slot />
</dvi>
</div>
1
2
3
<overlay>
Hello world!
</overlay>

Vue 提供了一个特殊的组件可以把其转换为任意的组件:component 组件。只需要将它的 is prop 设置为一个组件名或组件定义对象,甚至是一个 HTML 标签,component 组件就会变为响应的内容:

动态修改组件

1
2
3
<overlay v-if="activeOverlay">
<component :is="'overlay-content-'+activeOverlay" :player="currentPlayer" >
</overlay>

当 targetHeight 属性发生改变时,就开始播放动画

1
2
3
4
5
6
7
8
9
10
11
12
watch:{
targetHeight(newValue,oldValue){
const vm = this;
new TWEEN.Tween({value:oldValue})
.easing(TWEEN.Easing.Cubi.InOut)
.to({value: newValue},500)
.onUpdata(function(){
vm.height = this.value.toFixed(0)
})
.start()
}
}

高级项目设置

4.1 设置开发环境

安装 Node.js 和 npm

4.1.1 安装官方命令行工具 vue-cli

安装 vue-cli 并将其作为一个全局的包:

1
2
3
4
5
npm install -g vue-cli

// 打印 vue-cli 打印版本
vue -V
vue --version

项目脚手架

推荐的官方模板是 webpack 模板,具有使用 Vue 创建整个 SPA(单页面应用)所需的全部功能。使用 webpack-simple 并逐步引入功能。

渲染函数

Vue 使用一个虚拟 DOM 的实现,用树状结构的 JavaScript 对象来构建虚拟 DOM。然后 Vue 将虚拟 DOM 应用到真实浏览器的 DOM 上,所用方法是计算两者之间的差距。这尽可能地避免了 DOM 操作,因为 DOM 操作通常是主要的性能瓶颈。

按照惯例,h 是 createElement 的别名,这是编写 JSX 时非常常见和必须的。它得名于使用 JavaScript 描述 HTML 的技术 - Hyperscript。

createElement(或称 h)方法最多需要 3 个参数,
(1)第一个参数是元素类型。它可以是一个 HTML 标签名称(比如 div),在应用中注册过得组件名称,或者直接就是一个组件定义对象。
(2)第二个参数是可选的。它是一个定义了属性、prop、事件监听器等的数据对象。
(3)第三个参数也是可选的。它可以是简单的纯文本,也可以是一个用 h 创建的其他元素的数据。

示例:

1
2
3
4
5
6
7
8
9
10
11
render(h){
return h('ul',{'class':'movies'},[
h('li',{'class': 'movie'},'Start Wars'),
h('li',{'class':'movie'},'Blade Runner'),
])
}

<ul class='movies'>
<li class='movie'>Star Wars</li>
<li class='movie'>Blade Runner</li>
</ul>

4.2.4 配置 Babel

Babel 是一个 JavaScript 代码编辑工具,以便我们在旧版和最新版的浏览器中使用新特性(如 JSX 或箭头函数)。建议在所有正式的 JavaScript 项目中使用 Babel。

polyfill 是用于检查特性在浏览器中是否可用的代码;如果不可用,它将实现这个特性,使其可以像原生的一样工作。

1.Babel Vue 预览

安装并使用 babel-preset-vue

1
npm i -D babel-preset-vue

主要的 Babel 配置是在项目根目录下已存在的 .babelrc JSON 文件中完成的。

打开这个 .babelrc 文件并将 vue 预览添加到相应的列表中:

1
2
3
4
5
6
7
{
"presets": {
["env",{"modules": false}],
"stage-3",
"vue"
}
}

polyfill

添加 Babel polyfill,以便在旧浏览器中使用新的 JavaScript 特性。

在开发依赖中安装 babel-polyfill 包。

1
npm i -D babel-polyfill

在 src/main.js 文件的开头将其导入

import ‘babel-polyfill’

这将为浏览器启用所有必要的 polyfill。

4.2.5 更新依赖

1、手动更新

1
2
3
4
// 要检查项目中使用的包是否有新的版本,可以在根文件夹中运行以下命令:

npm outdated
npm install
不要忘记阅读你所更新包的更改日志!可能会有你希望了解的破坏性改变或改善。

2.自动更新

1
npm update
该命令只会更新与 package.json 文件中所指定版本兼容的版本。如果你想将包更新为其他版本,则需要手动执行。

3.更新 Vue

更新包含核心库的 Vue 包时,你也应该更新 vue-template-compiler 包。它是使用 webpack (或其他构件工具)时编译所有组件模板的包。

这两个包必须始终处于相同的版本。例如,如果你使用 vue 2.5.3 ,那么 vue-template-complier 也应该是版本 2.5.3.

4.2.6 为生产而构建

1
npm run build

默认情况下,使用 webpack-simple 模板时,它会将 JavaScript 文件输出到项目的 /dist 文件夹中。

4.3 单文件组件

Vue 具有自己的格式,名为单文件组件(SFC)。该格式由 Vue 团队创建,文件扩展名为 .vue。允许每个文件编写一个组件,将模板以及该组件的逻辑和样式集中在一个位置,这里的主要优势在于,每个组件都明显独立,更易于维护,易于共享。

单文件组件使用类似 HTML 的语法描述 Vue 组件。它可以包含 3 种类型的根块

1
2
3
<template>,使用我们已经用过的模板语法描述组件的模板;
<script>,其中包含组件的 JavaScript 代码;
<style>, 其中包含组件使用的样式。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<p>{{ message }}</p>
<input v-model="message" />>
</div>
</template>
<script>
export default {
data() {
return {
message: "Hello world"
};
}
};
</script>
<style>
p {
color: grey;
}
</style>

编辑 main.js 文件,并使用 import 关键字导入单文件组件;

1
import Test from './Test.vue'

移除 render 选项,使用对象展开运算符复制 Test 组件的定义:

1
2
3
4
new Vue({
el: '#app',
...Test
})
组件添加到应用程序的方法:使用 JavaScript 展开运算符,因此 ...App表达式将属性复制到应用定义对象。它的主要优点是,在开发工具中不再用无用的顶层组件;它会成为我们的根组件。

4.3.1 模板