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项目团队开发的一些基本配置封装分享

Nuxt 从入门到实践

Nuxt 常用配置项

配置 IP 和端口

1
2
3
4
5
6
7
8
9
10
//  package.json
"scripts": {
// ...
},
"config":{
"nuxt":{
"host":"127.0.0.1",
"port":"1818"
}
},

配置全局CSS

1
2
3
4
5
6
7
//  nuxt.config.js
head: {
// ...
},
css: [
'~assets/css/normailze.css',
],

配置webpack的loader

在nuxt.config.js里是可以对webpack的基本配置进行覆盖的

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
//  nuxt.config.js
build: {
loaders:[
{
test:/\.(png|jpe?g|gif|svg)$/,
loader:"url-loader",
query:{
limit:10000,
name:'img/[name].[hash].[ext]'
}
}
],

/*
** Run ESLint on save
*/
extend (config, { isDev, isClient }) {
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
}

Nuxt 路由

Nuxt.js 路由会根据 pages 目录中提供的Vue文件自动为您生成配置。

路由跳转传参

1
2
3
4
5
6
7
<template>
<!--路由跳转传参-->
<nuxt-link :to="{name:'news',params:{newsId:3306}}">NEWS</nuxt-link>

<!--路由接收参数-->
<p>NewsID:{{$route.params.newsId}}</p>
</template>

动态路由跳转传参

以下画线为前缀的Vue文件就是动态路由,然后在文件里边有 $route.params.id来接收参数

1
2
3
<template>
<nuxt-link :to="{name:'news-id',params:{id:123}}">News-1</nuxt-link>
</template>

动态参数校验

1
2
3
4
5
6
7
8
<script>
export default {
validate ({ params }) {
// Must be a number
return /^\d+$/.test(params.id)
}
}
</script>

路由切换动画

全局动画

1
2
3
4
5
6
7
/* assets/css/main.css */
.page-enter-active, .page-leave-active {
transition: opacity 2s;
}
.page-enter, .page-leave-active {
opacity: 0;
}
1
2
// nuxt.config.js
css:['assets/css/main.css'],

单页动画

1
2
3
4
5
6
7
8
9
/* assets/css/main.css */
.test-enter-active, .test-leave-active {
transition: all 2s;
font-size:12px;
}
.test-enter, .test-leave-active {
opacity: 0;
font-size:40px;
}
1
2
3
4
5
<script>
export default {
transition:'test'
}
</script>

Views 布局

默认模板

根目录下创建一个 app.html 就可实现

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 读取的是nuxt.config.js里的信息 -->
{{ HEAD }}
</head>
<body>
<p>jspang.com 技术胖的博客</p>
<!-- pages文件夹下的主体页面 -->
{{ APP }}
</body>
</html>

默认布局

1
2
3
4
5
6
7
<!-- layouts/default.vue -->
<template>
<div>
<p>JSPang.com 技术胖的博客</p>
<nuxt/>
</div>
</template>

自定义布局

例如:创建博客布局 在layouts 新建一个 blog.vue。

1
2
3
4
5
6
7
<!--layouts/blog.vue-->
<template>
<div>
<div>My blog navigation bar here</div>
<Nuxt />
</div>
</template>

使用博客布局

1
2
3
4
5
6
7
8
9
10
<!--pages/posts.vue-->
<template>
<!-- Your template -->
</template>
<script>
export default {
layout: 'blog'
// page component definitions
}
</script>

自定义错误页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--layouts/error.vue-->
<template>
<div>
<h1 v-if="error.statusCode === 404">Page not found</h1>
<h1 v-else>An error occurred</h1>
<NuxtLink to="/">Home page</NuxtLink>
</div>
</template>

<script>
export default {
props: ['error'],
layout: 'error' // you can set a custom layout for the error page
}
</script>

SEO meta、title

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
export default {
data(){
return{
title:this.$route.params.title,
}
},
// 独立设置 head 信息
head(){
return{
title:this.title,
meta:[
{hid:'description',name:'news',content:'This is news page'}
]
}
}
}
</script>
注意:为了避免子组件中的meta标签不能正确覆盖父组件中相同的标签而产生重复的现象,建议利用 hid 键为meta标签配一个唯一的标识编号。

数据请求

安装 Axios

1
npm install axios --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
<template>
<div>
<h1>姓名:{{info.name}}</h1>
<h2>年龄:{{info.age}}</h2>
<h2>兴趣:{{info.interest}}</h2>
</div>
</template>
<script>
import axios from 'axios'
export default {
data(){
return {
name:'hello World',
}
},
// promise
asyncData(){
return axios.get('https://api.myjson.com/bins/8gdmr')
.then((res)=>{
console.log(res)
return {info:res.data}
})
}
// await
async asyncData(){
let {data}=await axios.get('https://api.myjson.com/bins/8gdmr')
return {info: data}
}
}
</script>

静态资源的引入及打包

直接引入图片

1
2
3
<template>
<div><img src="~static/logo.png" /></div>
</template>

css 图片的引入

1
2
3
4
5
6
7
<style>
.diss{
width: 300px;
height: 100px;
background-image: url('~static/logo.png')
}
</style>

打包静态HTML并运行

1
npm run generate

live-server 可以查看打包后的文件


相关资料
[代码沙盒]路由
[代码沙盒]Views布局使用
技术胖Nuxt.js教程开启SSR渲染
[视频]技术胖Nuxt.js免费视频教程 开启SSR渲染

You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.