Vue 全家桶 - Webpack / Vue / Vue-cli / Vue-router / Vuex / Axios / Element UI
CSDN - 盛洪宇(技术胖) - Vue2.x从入门到实战
CSDN - 盛洪宇(技术胖) - Vue2.x从入门到实战
创建时间:2021年7月30日
最后一次修改时间:2021年7月31日
有关于 vue 的代码是需要遵循 ESLint 规范的,本篇由于适应 MarkDown 代码块,缩进方式发生了改变,敬请谅解
ESLint 规范 -indent_style = space \n indent_size = 2
MarkDown 代码块 -indent_style = tabs \n indent_size = 4
Vue 内部指令
- 开发版本 - 包含完整的警告和调试模式
- 生产版本 - 删除了警告,进行了压缩
# Shell
cnpm install live-server -g
npm init # 生成 package.json 文件
live-server
- v-if & v-else & v-show
- v-if: 判断是否加载,可以减轻服务器的压力,在需要时加载。
- v-show:调整css dispaly属性,可以使客户端操作更加流畅。display:none
<div id="app">
<div v-if='isLogin'>您好:已登录</div>
<div v-else>请登录后操作</div>
<div v-show='isShow'>展示内容</div>
</div>
var app = new Vue({
el: '#app',
data: {
isLogin: true,
isShow: true
}
})
- v-for
<div id="app">
<ul>
<li v-for='item in itemsSort'>{{item}}</li>
</ul>
<ul>
<li v-for='(item, index) in studentsSort'>
{{index}} : {{item.name}} - {{item.age}}
</li>
</ul>
</div>
var app = new Vue({
el: '#app',
data: {
items: [6, 5, 3, 7, 1, 2, 9, 8, 4],
students: [
{name: 'zhang', age: '23'},
{name: 'li', age: '43'},
{name: 'wang', age: '16'},
{name: 'sun', age: '7'}
]
},
computed: {
itemsSort: function () {
return this.items.sort(sortNumber)
},
studentsSort () {
return sortByKey(this.students, 'age')
}
}
})
function sortNumber (a, b) {
return a-b
}
function sortByKey (array, key) {
return array.sort(function(a, b) {
return a[key] - b[key]
})
}
- v-text & v-html
v-html 容易引起 xss 攻击 ,特别是不能用到表单里面
<div id="app">
<div v-text='message'></div>
<div v-html='dodo'></div>
</div>
var app = new Vue({
el: '#app',
data: {
message: 'v-text',
dodo: '<span style="color:red;">v-html</span>'
}
})
- v-on 前缀 @ 绑定事件监听器
- click
- keyup.键盘键值表
- input
<div id="app">
<p>本场比赛得分: {{fenshu}}</p>
<div>
<button v-on:click='add' @keyup.187='add'>加分</button>
<button v-on:click='minus'>减分</button>
</div>
<input type="text" v-model='fenshu2' v-on:keyup.enter='onEnter'>
<input type="text" v-model='fenshu2' v-on:input='onEnter'>
</div>
var app = new Vue({
el:'#app',
data: {
fenshu: 0,
fenshu2: 1
},
methods: {
add: function() {
this.fenshu++
},
minus: function() {
if (this.fenshu > 0) {
this.fenshu --
}
},
onEnter: function() {
if (this.fenshu2 > 0) {
this.fenshu += parseInt(this.fenshu2)
}
}
}
})
document.onkeydown = function(e) {
if (e.keyCode === 187) { // 键盘键值表 187 对应 + 键
// app.add()
}else if(e.keyCode == 189) { //键盘键值表 189 对应 - 键
app.minus()
}
}
- v-model 绑定数据源
- 修饰符
- .lazy:取代 input 监听 change 事件。
- .number:输入字符串转为数字。
- .trim:输入去掉首尾空格。
- 特殊标签
- input - checkbox - true/false
- input - checkbox - Array
- input - radio - String - value
- input
- 修饰符
<div id="app">
<p>原始文本信息: {{message}}</p>
<h4>文本框</h4>
<div>
<p>v-model <input type="text" id='vmodel' v-model='message'></p>
<p>v-mode:lazy <input type="text" v-model.lazy='message'></p>
<p>v-model:number <input type="text" v-model.number='message'></p>
<p>v-model:trim <input type="text" v-model.trim='message'></p>
</div>
<h4>文本域</h4>
<textarea cols="30" rows="4" v-model='message'></textarea>
<h4>多选框</h4>
<div style='user-select:none'>
<input type="checkbox" id='sunny' v-model='sunny'>
<label for="sunny">今天是晴天</label> <span>sunny = {{sunny}}</span>
</div>
<div style='user-select:none'>
<input type="checkbox" id='fruitApple' value='苹果' v-model='fruit'>
<label for="fruitApple">苹果</label>
<input type="checkbox" id='fruitBanana' value='香蕉' v-model='fruit'>
<label for="fruitBanana">香蕉</label>
<input type="checkbox" id='fruitOrange' value='橘子' v-model='fruit'>
<label for="fruitOrange">橘子</label>
<div>水果: {{fruit}}</div>
</div>
<h4>单选框</h4>
<div style='user-select:none'>
<input type="radio" id='sexMan' value='俊男爱靓女' v-model='sex' />
<label for="sexMan">男 Man</label>
<input type="radio" id='sexFemale' value='美女与野兽' v-model='sex' />
<label for="sexFemale">女 Female</label>
<div>性别: {{sex}}</div>
</div>
<h4>拓展: v-model & @input</h4>
<input type="text" v-model='test' v-on:input='testInput'>
</div>
var app = new Vue({
el: '#app',
data: {
message: 'hello world!',
sunny: true,
fruit: [],
sex: '请选择您的喜好',
test: null
},
methods: {
testInput: function (e) {
e.target.value = e.target.value.replace(/[^\d]/g, '')
console.log('input:' , e.target.value)
console.log('test:', this.test)
}
}
})
document.onkeydown = function (e) {
if (document.activeElement == document.body) { // 判断当前焦点元素是否为body
// 法一
if (e.keyCode == 83) { // 键盘键值表 对应 s 键
document.getElementById('sunny').click()
}else if (e.keyCode == 49) { // 键盘键值表 对应 1 键
document.getElementById('fruitApple').click()
}else if (e.keyCode == 50) { // 键盘键值表 对应 2 键
document.getElementById('fruitBanana').click()
}else if (e.keyCode == 51) { // 键盘键值表 对应 3 键
document.getElementById('fruitOrange').click()
}else if (e.keyCode == 77) { // 键盘简直表 对应 m 键
document.getElementById('sexMan').click()
}else if (e.keyCode == 70) { // 键盘键值表 对应 f 键
document.getElementById('sexFemale').click()
}
// 法二
// let keyCodeArray = [83, 49, 50, 51, 77, 70]
// 键盘键值表 83-s键 49-1键 50-2键 51-3键 77-m键 70-f键
// let idArray = ['sunny', 'fruitApple', 'fruitBanana', 'fruitOrange', 'sexMan', 'sexFemale']
// 未完待续
}
}
- v-bind 前缀 :
- data 数据 小驼峰
<div id="app">
<p><img v-bind:class='[imgBorder, imgBackground]' :src='imgSrc'></p>
<p><a :href="aHref">首页</a></p>
<hr>
<div :class='[{fontRed: fontRedOK}, userSelect]'>
<span>class 判断 数组</span>
<input type='checkbox' id='fontRedOK' v-model='fontRedOK'>
<label for='fontRedOK'>fontRedOK = {{fontRedOK}}</label>
</div>
<div :class='[fontRedOK ? fontRed : fontBlue, userSelect]'>
<span>class 三元运算符 数组</span>
<label>fontRedOK = <input type='checkbox' v-model='fontRedOK'>{{fontRedOK}} ? fontRed : fontBlue</label>
</div>
<div :style='{color: red, fontSize}'>Style</div>
<div :style='styleObject'>Style 对象</div>
</div>
var app = new Vue ({
el: '#app',
data: {
imgSrc: 'https://csdnimg.cn/release/educms/public/img/logo-small.9d4c95c9.png',
imgBackground: 'imgBackground',
imgBorder: 'imgBorder',
aHref: '/',
fontRed: 'fontRed',
fontRedOK: true,
userSelect: 'userSelect',
fontBlue: 'fontBlue',
red: 'red',
fontSize: '20px',
styleObject: {
color: 'red',
fontSize: '20px'
}
}
})
.imgBorder {
border: 1px solid black;
}
.imgBackground {
background: black;
}
.fontRed {
color: red;
}
.fontBlue {
color: blue;
}
.userSelect {
user-select: none;
}
- v-pre & v-cloak & v-once
- v-pre 在模板中跳过vue的编译,直接输出原始值
- v-cloak 在vue实例结束编译时从绑定的html元素上移除,经常和 display:none 配合使用
- v-once 在第一次加载DOM时进行渲染,渲染完成后视为静态内容,跳出后续的渲染过程
<div id='app'>
<div v-pre>{{message}}</div>
<div v-cloak>渲染完成后,才显示!</div>
<div v-once>{{message}} 渲染一次</div>
<div><input type="text" v-model='message'></div>
<div>{{message}}</div>
</div>
var app = new Vue ({
el: '#app',
data: {
message: 'hello world!'
}
})
[cloak] {
display:none;
}
Vue 全局API
Vue内置的一些全局API
先声明全局变量或者直接在Vue上定义一些新功能
在构造器外部用Vue提供给我们的API函数来定义新的功能
- Vue.directive 自定义指令
- el
- binding - rawName / name / expression / value / def
- 生命周期(钩子函数) - bind / inserted / update /componentUpdated / unbind
<div id="app">
<div v-color='color'>{{num}}</div>
<p><button @click='add'>add</button></p>
<p><button onclick='unbind()'>解绑</button></p>
</div>
function unbind() {
app.$destroy()
}
Vue.directive('color', {
bind: function(el, binding) {
console.log('1 - bind');
console.log(el);
console.log(binding);
},
inserted: function() {
console.log('2 - inserted');
},
update: function(el, binding) {
console.log('3 - update');
el.style = 'color:' + binding.value;
},
componentUpdated: function() {
console.log('4 - componentUpdated');
},
unbind: function(el, binding) {
console.log('5 - unbind');
el.style = 'color: wblack';
}
})
var app = new Vue({
el: '#app',
data: {
num: 10,
color: 'blue'
},
methods: {
add: function() {
this.num++
}
}
})
- Vue.extend(option)
扩展实例构造器 预设了部分选项的Vue实例构造器
经常服务于Vue.component用来生成组件
当在模板中遇到该组件名称作为标签的自定义元素时,会自动调用“扩展实例构造器”来生产组件实例,并挂载到自定义元素上
<div id="app"></div>
<home></home>
var home = Vue.extend({
template: '<p><a :href="homeURL">{{homeName}}</a></p>',
data: function() {
return {
homeName: '首页',
homeURL: '/'
}
}
})
new home().$mount('#app')
new home().$mount('home')
- Vue.set
在构造器外部操作构造器内部的数据、属性或者方法
data 处引用构造器外部数据
外部改变数据的三种方法: Vue.set / Vue 对象 / 外部数据
由于Javascript的限制,Vue不能自动检测以下变动的数组
- 利用索引直接设置一个项时,vue不会自动更新
- 修改数组的长度时,vue不会自动更新
<div id="app">
{{num}}
<ul>
<li v-for='item in arr'>{{item}}</li>
</ul>
<button onclick='add()'>修改</button>
</div>
var outData = {
num: 10,
arr: [10, 20, 30]
}
function add() {
// Vue.set(outData, 'num', 20)
// outData.num++
app.num++
// app.arr[1]++ // Vue可能会检测不到
Vue.set(app.arr, 1, 40)
// Vue.set(app.arr[1]++)
}
var app = new Vue({
el: '#app',
data: outData
})
- Vue 生命周期(钩子函数)
- 10个生命周期函数,在vue的每个阶段操作数据或者改变内容
- beforeCreat / created
- bedoreMount / mounted
- beforeUpdate / updated
- activated / deactivated
- beforeDestroy / destroyed
<div id="app">
{{count}}
<p><button @click='add'>add</button></p>
<p><button onclick='app.$destroy()'>销毁</button></p>
<p><button @click='destroy'>销毁</button></p>
</div>
var app = new Vue({
el: '#app',
data: {
count: 10
},
methods: {
add: function() {
console.log('1 - beforeAdd');
this.count++;
console.log('2 - added');
},
destroy: function() {
this.$destroy()
}
},
beforeCreate: function() {
console.log('1 - beforeCreate');
},
created: function() {
console.log('2 - created');
},
beforeMount: function() {
console.log('3 - beforeMount');
},
mounted: function() {
console.log('4 - mounted');
},
beforeUpdate: function() {
console.log('5 - beforeUpdated');
},
updated: function() {
console.log('6 - updated');
},
activated: function() {
console.log('7 - activated');
},
deactivated: function() {
console.log('8 - deactivated');
},
beforeDestroy: function() {
console.log('9 - beforeDestroy');
},
destroyed: function() {
console.log('10 - destryoed');
},
})
- Template 模板
- 构造器 template 选项
- template 标签
- script 标签
<!-- 第一种方式 选项模板 -->
<div id="app">
{{message}}
</div>
<!-- 第二种方式 Template 标签模板 -->
<template id='template2'>
<h2 style='color:blue'>第二种方式 Template 标签模板</h2>
</template>
<!-- 第三种方式 Script 标签模板 -->
<script type='x-template' id='template3'>
<h2 style='color:green'>第三种方式 Script 标签模板</h2>
</script>
<!-- <script type='x-template' id='template4' src='web文件形成一个模板'></script> -->
var template5 = `<p>This is Template5.</p>`
var app = new Vue({
el: '#app',
data: {
message: 'hello world!'
},
// template: `
// <h2 style='color:red'>第一种方式 选项模板</h2>
// `
// template: '#template2'
template: '#template3'
// template: '#template4'
// template: template5
})
- Component 组件
- 全局注册组件 - Vue.component - template
- 局部注册组件 - components - template
自定义组件 制作自定义的标签
组件注册的是一个标签,指令注册的是标签里的一个属性
<div id="app">
<comp1></comp1>
<comp2></comp2>
</div>
Vue.component('comp1', {
template: `<div style='color:red'>全局 component 组件</div>`
})
var app = new Vue({
el: '#app',
components: {
'comp2': {
template: `<div style='color:blue'>局部 component 组件</div>`
}
}
})
- Component 组件 props 属性
props 选项 设置和获取标签上的属性值 小驼峰
<div id="app">
<panda from-here='China' sex='Gentleman' v-bind:name='name'></panda>
</div>
var app = new Vue({
el: '#app',
data: {
name: 'xiaoPan'
},
components: {
'panda': {
template: `
<p style='color:blue'>panda is {{fromHere}} , sex is {{sex}} , name is {{name}}.</p>
`,
props: ['fromHere', 'sex', 'name']
}
}
})
- Component 父子组件
<div id="app">
<panda></panda>
</div>
var cityComponent = {
template: `<p style='color:blue'>SiChuan in China.</p>`
}
var pandaComponent = {
template: `
<div>
<p style='color:red'>panda from china.</p>
<city></city>
</div>
`,
components: {
'city': cityComponent
}
}
var app = new Vue({
el: '#app',
components: {
'panda': pandaComponent
}
})
- Component 标签
<div id="app">
<component :is='who'></component>
<p><button @click='changeComponent'>change component</button></p>
</div>
var componentA = {
template: `<p style='color:red'>This is componentA</p>`
}
var componentB = {
template: `<p style='color:blue'>This is componentB</p>`
}
var componentC = {
template: `<p style='color:green'>This is componentC</p>`
}
var app = new Vue({
el: '#app',
data: {
who: 'componentA'
},
components: {
'componentA': componentA,
'componentB': componentB,
'componentC': componentC
},
methods: {
changeComponent: function () {
if (this.who == 'componentA') {
this.who = 'componentB'
} else if (this.who == 'componentB') {
this.who = 'componentC'
} else if (this.who == 'componentC') {
this.who = 'componentA'
}
}
}
})
Vue 选项操作
- propsData
<header></header>
var header = Vue.extend({
template: `<p>{{message}} {{a}}</p>`,
data: function() {
return {
message: 'hello world!'
}
},
props: ['a']
})
new header({
propsData:{
a:10
}
}).$mount('header')
- computed
- get 默认
- set
computed和methods的区别
computed与methods的异同
Vue中computed和methods的区别
vue中computed和methods区别
深入理解配置选项之data和methods| 重学Vue3
区别 | methods | computed |
---|---|---|
响应式 | 非响应式 | 响应式 |
第一次调用 | 页面加载时 | 系统运行时 |
执行 | 跟随data实例值变化而执行 | 数值发生变化时 |
返回值 | 可写可不写 | 视为一个变量值 |
– | – | 以对象的属性方式存在的,在视图层直接调用就可以得到值 |
调用 | methods定义的成员必须以函数形式调用,视为一个方法 | computed定义的成员像属性一样访问,视为一个变量值 |
缓存 | 每次调用时都要执行 | 缓存值不变,不会重新调用(其引用的响应式属性发生改变时才会重新计算) |
<div id="app">
{{newPrice}}
<ol>
<li v-for='item in reverseNews'>{{item.title}} - {{item.data}}</li>
</ol>
</div>
var newsList = [
{title: 'news1', data: '2021-1'},
{title: 'news2', data: '2021-2'},
{title: 'news3', data: '2021-3'},
{title: 'news4', data: '2021-4'}
]
var app = new Vue({
el: '#app',
data: {
price: 100,
newsList: newsList
},
computed: {
newPrice: function () {
return '人民币' + this.price + '元'
},
reverseNews: function () {
return this.newsList.reverse()
}
}
})
- Methods
- $event 点击鼠标的一些事件和属性
- native 给组件绑定构造器里的原生事件
vue @click.native和@click.stop和@click.self
<div id="app">
{{count}}
<p><button @click='add(10, $event)'>add</button></p>
<p><btn @click.native='add(50)'></btn></p>
</div>
<p><button onclick='app.add(100)'>外部 add</button></p>
var btn = {
template: `<button>组件 原生 add</button>`
}
var app = new Vue({
el: '#app',
data: {
count: 10
},
components: {
'btn': btn
},
methods: {
add: function (num, event) {
if (num > 0) {
this.count += num;
} else {
this.count++;
}
console.log(event);
}
}
})
- Watch
<div id="app">
<p>今日温度: {{temperature}}℃</p>
<p>穿衣建议: {{clothing}}</p>
<p style='user-select:none'>
<button @click='add'>增加温度</button>
<button @click='minus'>减少温度</button>
</p>
</div>
var app = new Vue({
el:'#app',
data: {
temperature: 15,
clothing: '夹克长裙'
},
methods: {
add: function () {
this.temperature += 5;
},
minus: function () {
this.temperature -= 5;
}
},
// 构造器
watch: {
temperature: function (newVal, oldVal) {
if (newVal > 26) {
this.clothing = 'T恤短袖'
} else if (newVal > 1) {
this.clothing = '夹克长裙'
} else {
this.clothing = '棉衣羽绒服'
}
}
}
})
// 实例属性
// app.$watch('temperature', function (newVal, oldVal) {})
- Mixins
- 已经写好了构造器,需要增加方法或者临时使用的方法,用混入会减少源代码的污染
- 很多地方都会用到的公用方法,用混入的方法可以减少代码量,实现代码重用
- 调用先后顺序: 方法 (有原生方法只执行原生方法) | 生命周期 (全局 Vue.mixin - mixins - 原生)
<div id="app">
{{num}}
<p><button @click='add'>add</button></p>
</div>
var addConsole = {
updated: function () {
console.log('数据发生变化 , num = ' + this.num)
},
created: function () {
console.log('created')
}
}
Vue.mixin({
updated: function () {
console.log('全局 updated , num = ' + this.num)
},
created: function () {
console.log('全局 created')
}
})
var app = new Vue({
el: '#app',
data: {
num: 10
},
methods: {
add: function () {
this.num++
}
},
updated: function () {
console.log('原生 updated , num = ' + this.num)
},
created: function () {
console.log('原生 created')
},
mixins: [addConsole]
})
- Extends
- 执行顺序: 方法 (有原生只执行原生方法,否则执行扩展方法) | 生命周期 (扩展 - 原生)
- delimiters - 改变插值的符号
<div id="app">
${num}
<p><button @click='add'>add</button></p>
</div>
var extendsObj = {
methods: {
add: function () {
this.num++;
console.log('扩展 methods');
}
},
updated: function () {
console.log('扩展 updated');
}
}
var app = new Vue({
el: '#app',
data: {
num: 10
},
methods: {
add: function () {
this.num++;
console.log('原生 methods');
}
},
updated: function () {
console.log('原生 updated');
},
extends: extendsObj,
delimiters: ['${', '}']
})
Vue 实例和内置组件
- 属性
<script src='https://code.jquery.com/jquery-3.6.0.min.js'></script>
<div id="app">
{{message}}
</div>
var app = new Vue({
el:'#app',
data: {
message: 'hello world!'
},
methods: {
add: function() {
console.log('构造器内部的add方法。');
}
},
mounted: function() {
$('#app').html('我是 JQuery!'); // jQuery 需要引入jQuery
}
})
app.add();
- 方法
- $mount - 挂载扩展
- $destroy - 卸载
- $forceUpdate - 更新
- $nextTick - 数据修改,相当于一个钩子函数,和构造器里的updated生命周期很像
<div id="app"></div>
<p><button onclick='del()'>$destroy</button></p>
<p><button onclick='update()'>$forceUpdate</button></p>
<p><button onclick='tick()'>$nextTick</button></p>
var ex = Vue.extend({
template: `<p>{{message}}</p>`,
data: function() {
return {
message: 'hello world!'
}
},
mounted: function() {
console.log('mounted');
},
updated: function() {
console.log('updated');
},
destroyed: function() {
console.log('destroyed');
}
})
var e = new ex().$mount('#app');
function del() {
e.$destroy();
}
function update() {
e.$forceUpdate();
}
function tick() {
e.message = 'this is Vue!';
e.$nextTick(function() {
console.log('$nextTick');
})
}
- 事件
- 在构造器外部调用构造器内部的数据
- $on - 在构造器外部添加事件
- $once - 执行一次的事件
- $off - 关闭事件
<div id="app">
{{num}}
<p><button @click='add'>add</button></p>
</div>
<p><button onclick='minus()'>minus</button></p>
<p><button onclick='once()'>once</button></p>
<p><button onclick='off()'>off</button></p>
var app = new Vue({
el:'#app',
data: {
num: 10
},
methods: {
add: function() {
this.num++;
}
}
})
app.$on('minus', function() {
this.num--;
})
app.$once('once', function() {
this.num = 50;
})
function minus() {
app.$emit('minus');
}
function once() {
app.$emit('once');
}
function off() {
app.$off('minus')
}
- 内置组件 slot
- 标签的内容扩展,在自定义组件时传递给组件内容,组件接收内容并输出
<div id="app">
<net>
<span slot='url'>{{net.url}}</span>
<span slot='name'>This is name.</span>
<span slot='skill'>{{net.skill}}</span>
</net>
</div>
<template id='net'>
<div>
<p>url: <slot name='url'></slot></p>
<p>name: <slot name='name'></slot></p>
<p>skill: <slot name='skill'></slot></p>
</div>
</template>
var net = {
template: `#net`
}
var app = new Vue({
el: '#app',
data: {
net: {
url: 'This is netUrl.',
name: 'This is name.',
skill: 'This is Vue.'
},
num: 10
},
components: {
'net': net
}
})
Vue-cli
cnpm install vue-cli -g # -g 全局安装
vue init <template-name> <project-name> # 初始化项目
# <template-name>
# webpack | webpack-simple | browserify | browserify-simple | simple
vue init webpack vueCliTest
cd vueCliTest
cnpm install # 安装 package.json 依赖包 node_modules
# build 项目构建 webpack
# config 项目开发环境配置
# node_modules npm包管理器
# src 源码目录
# static 静态文件目录
# /src/main.js 整个项目的入口文件
# /src/router/index.js 路由文件
npm run dev # 开发 | package.json - scripts - dev
npm run build # 生产 | 生成 dist 目录
# dev | start | build 打包
# /.editorconfig
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
Vue-router
# 终端
npm list -g --depth 0 # 查看通过npm安装的包
cnpm install vue-cli -g # 淘宝镜像 cnpm 安装脚手架 vue-cli
vue init webpack vueCliTest # 初始化 vue 项目
cd vueCliTest
cnpm install vue-router --dev-save # 安装路由 vue-router
cnpm install # 如果 packgae.json 添加了新的插件与版本,可以使用此命令自动安装
npm run dev # 运行开发环境
# 修改启动端口 默认 8080
# /config/index.js - module.exports - dev - port
// /src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld' // /src/components/HelloWorld.vue
import Hi from '@/components/Hi'
import Hi1 from '@/components/Hi-1'
import Hi2 from '@/components/Hi-2'
import params from '@/comoponents/params'
import ErrorPage from '@/components/ErrorPage'
Vue.use(Router) // 全局使用
export default new Router({
mode: 'history', // history / hash 默认值,项目打包上线需要改回 hash 模式,否则容易出错
routes: [ // 路由 数组
{ // 每个链接是一个对象
path: '/', // 链接路径
name: 'HelloWorld', // 路由名称
// component: HelloWorld // 组件模板
components: { // 单页面多路由区域操作 对象
default: HelloWorld, // 默认路由
left: Hi1, // 对应 router-view - name='left'
right: Hi2 // 对应 router-view - name='right'
}
},
{
pth: '/Hi',
name: 'Hi',
component: Hi,
children: [ // 子路由 数组
{path: 'Hi-1', name: 'Hi-1', component: Hi1},
{path: 'Hi-2', name: 'Hi-2', component: Hi2}
]
},
{
path: '/params/:id(\\d+)/:title', // 利用 url 传递参数 id(\\d+) 正则表达式
name: 'params',
component: params,
beforeEnter: (to, from, next) => { // 路由中的钩子函数
console.log('index.js', to) // to 路由将要跳转的路径信息,信息包含在对象里
console.log('index.js', from) // from 路由跳转前的路径信息,信息包含在对象里
next() // next 路由的控制参数 常用的有 next(true) next(false)
// next({path: '/'})
}
},
{
path: '/goHome',
redirect: '/' // 重定向
},
{
path: '/goParams/:id(\\d+)/:title',
redirect: '/params/:id(\\d+)/:title' // 重定向时传递参数
},
{
path: '/',
alias: '/aliasHome' // alias 实现类似重定向的效果 注意不能跳转到首页
},
{
path: '*',
name: 'ErrorPage',
component: ErrorPage // 404 页面
}
]
}
<!-- /src/App.vue -->
<template>
<div id="app">
<!-- 编程式导航 -->
<div>
<button @click='goBack'>后退</button>
<button @click='goForward'>前进</button>
<button @click='goHome'>首页</button>
</div>
<div>
<router-link to="/">首页</router-link> | <!-- router-link 导航 to 链接路径 显示名称 -->
<router-link to="/Hi">Hi</router-link> |
<!-- <router-link to="/Hi/Hi-1">Hi-hi1<router-link> |-->
<!-- <router-link to="{name: xxx, params: {key: value}}">valueString</router-link> -->
<router-link :to="{name: 'Hi-1', params: {id: 1, name: 'Hi-1'}}">Hi-1</router-link> |
<router-link to="/Hi/Hi-2">Hi-2<router-link> |
<!-- 利用url传递参数 -->
<router-link to='/params/2/Welcome to params page!'>params</router-link> |
<!-- 重定向 -->
<router-link to='goHome'>goHome<router-link> |
<!-- 重定向时传递参数 -->
<router-link to='/goParams/3/I like Vue.js'>goParams</router-link> |
<!-- alias 实现类似重定向的效果 注意不能跳转到首页 -->
<router-link to='/aliasHome'>aliasHome</router-link>
<!-- 任何未在 index.js 有过明确定义的 url ,都会进入 404 页面 ErrorPage.vue -->
<router-link to='/xxxx'>ErrorPage<router-link>
</div>
<!-- transtion name='fade' class='fade-enter fade-enter-active fade-leave fade-leave-active' -->
<transition name='fade' mode='in-out'>
<router-view></router-view>
<!-- 单页面多路由区域操作 -->
<router-view name='left' style='float:left;width:50%;height:300px;background:#ccc;'></router-view>
<router-view name='right' style='float:right;width:50%;height:300px;background:#ddd;'></router-view>
</transition>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
goBack: function () {
this.$router.go(-1) // 后退
},
goForward: function () {
this.$router.go(1) // 前进
},
goHome: function () {
this.$router.push('/') // 导航到首页
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.fade-enter {
opacity: 0;
position: relative;
right: 100vw;
}
.fade-enter-active {
transition-property: all;
transition-duration: 4s;
transition-timing-function: ease-out;
}
.fade-enter-to {
opacity: 1;
position: relative;
right: 0;
}
.fade-leave {
opacity: 1;
}
.fade-leave-active {
transition-property: opacity;
transition-duration: 2s;
}
.fade-leave-to {
opacity: 0;
}
</style>
<!-- /src/components/Hi.vue -->
<template>
<div class='hello'>
<h1>{{message}}</h1>
<hr>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Hi',
data () {
return {
message: 'This is Hi page.
}
}
}
</script>
<!-- /src/components/Hi-1.vue -->
<template>
<div class='hello'>
<h1>{{message}}</h1>
<hr>
<p>{{$route.name}}</p>
<p>id: {{$route.params.id}}</p>
<p>name: {{$route.params.name}}</p>
</div>
</template>
<script>
export default {
name: 'Hi-1',
data () {
return {
message: 'This is Hi-1 page.
}
}
}
</script>
<style scoped>
</style>
<!-- /src/components/Hi-2.vue -->
<template>
<div>
<h2>{{msg}}</h2>
</div>
</template>
<script>
export default {
name:'Hi-2',
data () {
return {
msg: 'This is Hi-2 page!'
}
}
}
</script>
<!-- /src/components/params.vue -->
<template>
<div>
<h2>{{msg}}</h2>
<p>id: {{$route.params.id}}</p>
<p>title: {{$route.params.title}}</p>
</div>
</template>
<script>
export default {
name: 'params',
data () {
return {
msg: 'This is params page!'
}
},
// 模板中的钩子函数
beforeRouteEnter: (to, from, next) => { // 路由进入前的钩子函数
console.log('params.vue', '准备进入 params 路由模板')
next()
},
beforeRouterLeave: (to, from, next) => { // 路由离开前的钩子函数
consol.log('params.vue', '准备离开 params 路由模板')
next()
}
},
</script>
<!-- /src/components/Errorpage.vue -->
<template>
<div>
<h2>{{msg}}</h2>
</div>
</template>
<script>
export default {
name: 'ErrorPage',
data () {
return {
msg: 'Error: 404 page!'
}
}
}
</script>
mode | hash | history |
---|---|---|
URL | 协议://域名/#/路由 | 协议://域名/路由 |
页面刷新 | 不会出现404错误 | 会出现404错误 |
vue路由mode模式:history与hash的区别 | ||
vue中mode hash 和 history的区别 |
重定向 | redirect | alias |
---|---|---|
路由 | path: 'goHome', redirect: '/' | path: '/Hi', alias: '/aliasHi' |
导航 | <router-link to='goHome'>goHome</router-link> | <router-link to='aliasHi'>aliasHi</router-link> |
path:'/' | 可以跳转 | 不能跳转 |
<transition name='fade'>
CSS过渡类名
fade-enter:进入过渡的开始状态,元素被插入时生效,只应用一帧后立刻删除。
fade-enter-active:进入过渡的结束状态,元素被插入时就生效,在过渡过程完成后移除。
fade-leave:离开过渡的开始状态,元素被删除时触发,只应用一帧后立刻删除。
fade-leave-active:离开过渡的结束状态,元素被删除时生效,离开过渡完成后被删除。
Vuex 单页状态管理
cnpm install vuex --save # 利用 npm 包管理工具 安装 vuex 生产环境 --save 版本位置 package.json - dependencies
// /src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import helloWorld from '@/components/HelloWorld'
import Count from '@/components/Count'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/Count',
name: 'Count',
component: Count
}
]
})
<template>
<nav>
<a href='/'>首页</a>
<a href='/Count'>Count</a>
<a href='goBack'>后退</a>
<a href='goForward'>前进</a>
</nav>
<router-view></router-view>
{{route.name}}
</template>
<script>
export default {
name: 'App',
methods: {
goBack: function () {
this.$router.go(-1)
},
goForward: function () {
this.$router.go(1)
}
}
}
</script>
<style scope>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
a {
cursor: pointer;
color: blue;
text-decoration: underline;
}
a, button {
user-select: none;
}
</style>
// /src/vuex/store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
count: 1
}
const mutations = {
add: function (state, n = 1) {
state.count += n
},
reduce: function (state, n = 1) {
state.count -= n
}
}
const getters = { // 计算过滤操作
count: function (state) {
state.count += 0
return state.count
}
}
const actions = { // 异步修改状态
addAction (context) { // context 上下文独享
context.commit('add', 10)
setTimeout(() => { // 异步检验
context.commit('reduce')
}, 2000)
console.log('我比 reduce 先执行')
},
reduceAction ({commit}) { // {commit}
commit('reduce')
}
}
const moduleA = { // 声明模块组
state,
mutations,
getters,
actions
}
export default new Vuex.Store({ // 封装代码 让外部可以引用
modules: {
a: moduleA
}
})
<!-- /src/components/Count.vue -->
<template>
<div>
<h2>{{msg}}</h2>
<p>$store.state.count: {{$store.state.count}}</p>
<p>count(): {{count}}</p>
<p>
<button @click="$store.commit('add', 10)">addCount</button>
<button @click="reduce()">reduceCount</button>
</p>
<p>
<button @click='addAction'>addAction</button>
<button @click='reduceAction'>reduceAction</button>
</p>
</div>
</template>
<script>
import stroe from '@/vuex/store'
import { mapState, mapMutations, mapGetters, mapActions } from 'vuex'
export default {
name: 'Count',
data () {
return {
msg: 'Hello Vuex!'
}
},
// 法一 访问状态对象
// computed: {
// count: function () {
// return this.$store.state.count
// }
// },
// 法二 访问状态对象
// computed: mapState({
// count: function (state) {
// return state.count
// }
// }),
// 法三 访问状态对象
// computed: mapState({
// count: state => state.count
// }),
// 法四 访问状态对象
// computed: mapState(['count']),
computed: {
...mapState(['count']), // 访问状态对象 扩展运算符
// count: function () {
// return this.$store.state.count
// }
...mapGetters(['count']) // 计算过滤操作 扩展运算符
// count () {
// return this.$store.a.count
// }
},
// methods: mapMutations(['add', 'reduce']),
methods: {
...mapMutations(['add', 'reduce']), // 修改状态 扩展运算符
...mapActions(['addAction', 'reduceAction']) // 异步修改状态 扩展运算符
},
store
}
</script>
实战
- Mockplus 把我们的想法画出来
技术栈 - Vue + Webpack + Element + Axios + vueRouter
内容 - 快餐店的POS系统 收银模块
Vue - 主体
Webpack - 打包程序 构建工具
Element - UI 界面
Axios - 数据请求 后端交互
vueRouter - 路由系统
MockPlus 慕课 软件 免费
幕客 产品 Mockplus经典版
- Vue-cli 搭建开发环境
npm list -g --depth 0
cnpm install vue-cli -g
vue -V
vue init webpack # 已经有目录,在目录下可以省略项目名称
# Vue build - standalone | vue-router | ESLint
cnpm install
npm run dev
<style scoped>/* scoped 局部变量,只在当前模板生效 */</style>
- 搞定项目图标 Iconfont
Font Awesome
Google Play 图标设计规范
iconfont
图标库 - 官方图标库 - 天猫图标库 碎蜂
汉堡 店铺 购买 功能建议 会员卡 - 添加至项目 Pos - Font class
Unicode 非语义化 | Font class 语义化 | Symbol
<i class='icon iconfont icon-dianpu'></i>
<i class='icon iconfont icon-huiyuanqia'></i>
- 开启 Element 封印
Element 组件库
Element
# 项目目录下
cnpm install element-ui --save # --save 生产环境
npm run dev
- 利用 Element 快速布局
多标签栏 订单栏
- Axios 从远程读取数据
cnpm install axios --save
- 项目打包和上线
/config/index.js - module.exports - build - assetsPublicPath: ‘./’
/build/utils.js - 51 column - options.extract - publicPath: ‘…/…/’
cnpm run build
附录
Vue全家桶介绍
× 失效 技术胖 – Vue2.x学习路线-按次路线学习顺畅无比
技术胖 – 视频教程
CSDN 视频 – Vue2.x从入门到实战
博客园 – noticeable - 键盘键值对应表