<body>
<div id="app">
<h1>{{message}}</h1>
<h2>{{name}}</h2>
</div>
<script src="./vue.js"></script>
<script>
// let变量 / const常量
// 编程范式:声明式编程
const app = new Vue({
el: '#app', // 挂载管理元素
data: { // 定义数据
message: 'wdnmd',
name: 'test'
}
})
</script>
</body>
<body>
<div id="app">
<h1>{{name}}</h1>
<ul>
<li v-for="item in list">{{item}}</li>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: '电影列表',
list: ['战狼', '战狼2', '战狼3', '战狼4']
}
})
</script>
</body>
options 需要掌握的选项
el:
类型:string | HTMLElement
作用:决定 Vue 实例管理哪一个 DOM 对象
data:
类型:Object | Fuction (组件当中 data 必须是一个函数)
作用:Vue 实例对应的数据对象
methods:
类型:{[key:string] : Fuction}
作用:定义属于 Vue 的一些方法,可以在其他地方调用,也可以在指令中使用
Mustache 语法也就是 {{}}
,将数据插入到 HTML 页面中需要
表示元素或者组件只渲染一次,不会随着数据改版而改变
<body>
<div id="app">
<h1>{{name}}</h1>
<h1 v-once>{{name}}</h1>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: '小明'
}
})
app.name = '小红';
</script>
</body>
结果:
小红
小明
解析 HTML 内容并渲染
<body>
<div id="app">
<h1>{{html}}</h1>
<h1 v-html="html"></h1>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
html: '<a href="https://baidu.com">test</a>'
}
})
</script>
</body>
将数据显示在页面中,接收的值为 string 类型
<body>
<div id="app">
<h1>{{text}},Vue</h1>
<h1 v-text="text"></h1>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
text: '你好'
}
})
</script>
</body>
跳过元素的编译过程,显示原本内容
<body>
<div id="app">
<h1>{{text}}</h1>
<h1 v-pre>{{text}}</h1>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
text: '你好'
}
})
</script>
</body>
结果:
你好
{{text}}
解析之前不显示元素内容
<head>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<h1>{{text}}</h1>
</div>
<script src="./vue.js"></script>
<script>
setTimeout(function () {
const app = new Vue({
el: '#app',
data: {
text: '你好'
}
})
}, 1000);
</script>
</body>
缩写:@
预期:Function | Inline Statement | Object
参数:Event
修饰符:
用法:绑定事件侦听
<body>
<div id="app">
<h2>当前数为:{{num}}</h2>
<button @click="add()">+</button>
<button @click="pdd()">-</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num: 0
},
methods: {
add() {
this.num++;
},
pdd() {
this.num--;
}
},
})
</script>
</body>
可以阻止冒泡,当父元素与子元素同时存在某一属性,出发子元素属性不触发父元素属性
<body>
<div id="app" @click="click1">
<p>wdnmd</p>
<button @click.stop="click2">button</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
methods: {
click1() {
console.log("click1");
},
click2() {
console.log("click2");
}
},
})
</script>
</body>
阻止默认事件 ,进入自定义事件
<body>
<form action="/wdnmd">
<input type="submit" value="提交" @click.prevent="click1">
</form>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: 'form',
methods: {
click1() {
console.log("click1");
},
},
})
</script>
</body>
按键触发
<body>
<input type="text" @keyup.enter="click1">
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: 'input',
methods: {
click1() {
console.log("click1");
},
},
})
</script>
</body>
只触发一次
<body>
<input type="text" @keyup.enter.once="click1">
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: 'input',
methods: {
click1() {
console.log("click1");
},
},
})
</script>
</body>
动态修改标签的属性
语法糖可直接写成 :
<body>
<div id="app">
<img v-bind:src="img" alt="">
<a v-bind:href="url">{{text}}</a>
<!-- 语法糖写法 -->
<img :src="img" alt="">
<a :href="url">{{text}}</a>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
img: './lanye (51).jpg',
url: 'https://baidu.com',
text: 'wdnmd'
}
})
</script>
</body>
<body>
<div id="app">
<p v-if="score>=90">优</p>
<p v-else-if="score>=70">良</p>
<p v-else-if="score>=60">中</p>
<p v-else>差</p>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
score: 80,
},
})
</script>
</body>
通过 bool 变量来决定是否隐藏或显示元素,不过隐藏方式是 display 改为 none
<body>
<div id="app">
<span v-show="show">wdnmd</span>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
show: true
},
})
</script>
</body>
for 循环遍历
数组可获取 index 下标
对象可获取 key 与 value
<body>
<div id="app">
<p v-for="item in text1">{{item}}</p>
<hr>
<p v-for="(item, index) in text1">{{index + 1}}.{{item}}</p>
<hr>
<p v-for="(value, key) in text2">{{key}}-{{value}}</p>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
text1: ['小红', '小明', '小黄', '小狗'],
text2: {
name1: '小红',
name2: '小明',
name3: '小黄',
name4: '小狗'
}
},
})
</script>
</body>
官方推荐我们在使用 v-for 时,给对应的元素或组件添加上一个key属性
key的作用主要是为了高效的更新虚拟DOM
<body>
<div id="app">
<p v-for="(item, index) in text1" :key="item">{{item}}</p>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
text1: ['小红', '小明', '小黄', '小狗'],
},
})
</script>
</body>
如果不光有一个简单的 div,而是有一大堆元素,那么它们应该会形成一个 VNode Tree
<head>
<style>
.color {
color: red;
}
.size {
font-size: 1.5rem;
}
</style>
</head>
<body>
<div id="app">
<p v-bind:class="{color: wdnmd, size: wdnmd2}">wdnmd</p>
<button v-on:click="haha()">点老子</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
wdnmd: true,
wdnmd2: true,
},
methods: {
haha() {
app.wdnmd = !app.wdnmd;
app.wdnmd2 = !app.wdnmd2;
}
},
})
</script>
</body>
<body>
<div id="app">
<p v-bind:class="getclass()">wdnmd</p>
<button v-on:click="haha()">点老子</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
wdnmd: true,
wdnmd2: true,
},
methods: {
haha() {
app.wdnmd = !app.wdnmd;
app.wdnmd2 = !app.wdnmd2;
},
getclass: function() {
return {color: this.wdnmd, size: this.wdnmd2};
}
},
})
</script>
</body>
<body>
<div id="app">
<p :style="getclass()">wdnmd</p>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
getclass: function() {
return {color: 'red', 'font-size': '1.5rem'};
}
},
})
</script>
</body>
<body>
<div id="app">
<p :style="[sz]">wdnmd</p>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
sz: {backgroundColor: 'red', fontSize: '100px'}
},
})
</script>
</body>
<body>
<div id="app">
<p>{{wdnmd}}</p>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Kobe',
lastName: 'Bryant'
},
computed: {
wdnmd: {
set: function (str) {
console.log('set');
this.firstName = str;
},
get: function () {
console.log('get');
return this.firstName + ' ' + this.lastName;
}
}
},
})
</script>
</body>
methods 属于方法,每次调用都是重复执行
computed 可在 Vue 中生成缓存,结果一样则调用缓存数据,减少消耗
<body>
<div id="app">
<p>{{wdnmd1()}}</p>
<p>{{wdnmd1()}}</p>
<p>{{wdnmd1()}}</p>
<p>{{wdnmd1()}}</p>
<p>{{wdnmd1()}}</p>
<p>{{wdnmd2}}</p>
<p>{{wdnmd2}}</p>
<p>{{wdnmd2}}</p>
<p>{{wdnmd2}}</p>
<p>{{wdnmd2}}</p>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Kobe',
lastName: 'Bryant'
},
methods: {
wdnmd1: function () {
console.log('methods');
return this.firstName + ' ' + this.lastName;
}
},
computed: {
wdnmd2: function () {
console.log('computed');
return this.firstName + ' ' + this.lastName;
}
},
})
</script>
</body>
当在同一个父元素的 input ,切换的时候数据会复用
使用 key 给 input 赋值上不同的 key 即可解决复用问题
<body>
<div id="app">
<span v-if="show">
<label for="username">用户名:</label>
<input type="text" id="username" placeholder="请填写用户名" key="username">
</span>
<span v-else>
<label for="email">邮 箱:</label>
<input type="text" id="email" placeholder="请填写邮箱" key="email">
</span>
<button @click="show = !show">切换</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
show: true
},
})
</script>
</body>
使用这些方法可以响应式的去修改数组中的数据
<body>
<div id="app">
<ul>
<li v-for="(item, index) in text1">{{item}}</li>
<button @click="click">button</button>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
text1: ['小红', '小明', '小黄', '小狗'],
},
methods: {
click() {
// unshift() 数组最前面添加一个数据
this.text1.unshift("unshift");
// push() 数组最后面添加一个数据
this.text1.push("push");
// shift() 删除数组中第一个数据
this.text1.shift();
// pop() 删除数组中最后一个数据
this.text1.pop();
// splice() 删除/插入/替换 元素
// 删除:指定从哪开始,然后删除多少个元素
// 插入:指定从哪插入,第二个为0,第三个依次填入数据
// 替换:指定从哪开始替换,第二个为替换多少个数据,第三个依次为替换后的数据
this.text1.splice(1, 2, 'aaa', 'bbb')
// sort() 正序
this.text1.sort();
// reverse() 倒序
this.text1.reverse();
// Vue.set()修改
Vue.set(this.text1, 0, "aaa");
}
},
})
</script>
</body>
<head>
<style>
.active {
color: red;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item, index) in movies" :class="{active: index === currentIndex}" @click="click(index)">{{index}}.{{item}}</li>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
movies: ['wdnmd1', 'wdnmd2', 'wdnmd3', 'wdnmd4'],
currentIndex: 0,
},
methods: {
click(index) {
this.currentIndex = index;
}
},
})
</script>
</body>
定义过滤器
filters: {
price(price) {
return '¥' + price.toFixed(2);
}
}
调用过滤器
使用符号 |
分隔
<h1>总价格:{{countPrice | price}}</h1>
<body>
<div id="app">
<div v-if="books.length">
<table>
<thead>
<tr>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in books" :key="index">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>{{item.price | price}}</td>
<td>
<button @click="del(index)" :disabled="item.count <= 1">-</button>
{{item.count}}
<button @click="add(index)">+</button>
</td>
<td>
<button @click="remove(index)">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
<h2 v-else>购物车为空</h2>
<h1>总价格:{{countPrice | price}}</h1>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [{
id: 1,
name: 'wdnmd',
date: '2001-5-6',
price: 50.5,
count: 1
},
{
id: 2,
name: 'wdnmd',
date: '2001-5-6',
price: 50.5,
count: 1
}, {
id: 3,
name: 'wdnmd',
date: '2001-5-6',
price: 50.5,
count: 1
}, {
id: 4,
name: 'wdnmd',
date: '2001-5-6',
price: 50.5,
count: 1
}, {
id: 5,
name: 'wdnmd',
date: '2001-5-6',
price: 50.5,
count: 1
},
]
},
methods: {
add(index) {
this.books[index].count++;
},
del(index) {
this.books[index].count--;
},
remove(index) {
this.books.splice(index, 1);
}
},
computed: {
countPrice() {
let count = 0;
for(let i = 0; i < this.books.length; i++) {
count += this.books[i].price * this.books[i].count;
}
return count;
}
},
filters: {
price(price) {
return '¥' + price.toFixed(2);
}
}
})
</script>
</body>
自定义过滤规则,返回 bool 类型
let num = [10, 20, 30, 40, 50]
let numA = num.filter(function(n) {
return n < 30;
})
console.log(numA);
操作数据
let num = [10, 20, 30, 40, 50];
let numA = num.map(function(n) {
return n + 30;
})
console.log(numA);
对数组中所有的内容进行汇总
preValue:数组前一次返回的值
let numA = [1, 2, 3, 4, 5];
let numb = numA.reduce(function(value, n) {
return value + n;
}, 0)
console.log(numb);
v-model 会与 value 属性进行绑定
当 data 里面的值被修改同时会修改 v-model 绑定的值,同理 v-model 值被修改 data 里面的值也会修改
<body>
<div id="app">
<input type="text" v-model="msg">
{{msg}}
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
msg: "wdnmd"
}
})
</script>
</body>
<body>
<div id="app">
<input type="radio" v-model="sex" value="男">男
<input type="radio" v-model="sex" value="女">女
{{sex}}
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
sex: "男"
}
})
</script>
</body>
<body>
<div id="app">
<input type="checkbox" value="篮球" v-model="ball">篮球
<input type="checkbox" value="足球" v-model="ball">足球
<input type="checkbox" value="羽毛球" v-model="ball">羽毛球
<input type="checkbox" value="乒乓球" v-model="ball">乒乓球
{{ball}}
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
ball: []
}
})
</script>
</body>
<body>
<div id="app">
<select v-model="select">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="橙子">橙子</option>
</select>
{{select}}
<br />
<select v-model="select2" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="橙子">橙子</option>
</select>
{{select2}}
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
select: "",
select2: []
}
})
</script>
</body>
v-model.lazy
:失去焦点或者按下回车才更新数据
<input type="text" v-model.lazy="msg">
v-model.number
:数字格式
<input type="number" v-model.number="msg">
v-model.trim
:去掉左右空格
<input type="text" v-model.trim="msg">
Vue.extend
创建组件template
为组件 DOM 内容Vue.component
来注册全局组件;局部组件为 components:{标签名, 组件名}
组件就是一个vue实例,vue实例的属性,组件也可以有,例如data、methods、computed等。
生命周期钩子 | 详细 |
---|---|
beforeCreate | 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。 |
created | 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。 |
beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用。 |
mounted | el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。 |
beforeUpdate | 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。 |
updated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。 |
activated | keep-alive 组件激活时调用。 |
deactivated | keep-alive 组件停用时调用。 |
beforeDestroy | 实例销毁之前调用。在这一步,实例仍然完全可用。 |
destroyed | Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 |
可以在多个 vue 实例中使用,类似于全局变量
写法:Vue.component(标签名, 组件名)
<body>
<div id="app">
<wdnmd></wdnmd>
</div>
<script src="./vue.js"></script>
<script>
// 创建组件构造器对象
const test = Vue.extend({
template: "<h1>wdnmd</h1><h2>wdnmd</h2>"
})
// 注册组件(全局)
Vue.component('wdnmd', test)
const app = new Vue({
el: "#app",
})
</script>
</body>
只能在当前已挂载的 vue 对象中使用
写法:components:{标签名, 组件名}
<body>
<div id="app">
<wdnmd></wdnmd>
</div>
<script src="./vue.js"></script>
<script>
// 创建组件构造器对象
const test = Vue.extend({
template: "<h1>wdnmd</h1><h2>wdnmd</h2>"
})
const app = new Vue({
el: "#app",
// 注册组件(局部)
components: {
wdnmd: test
}
})
</script>
</body>
下方定义了 test1 与 test2 两个组件,test2 里面注册了 test1组件,然后局部注册了 test2 组件;当调用 test2 组件时,同时会调用 test1 组件,test2 与 test1 组件此时为父子组件
使用子父组件 template
代码需要 div 嵌套起来才能运行
<body>
<div id="app">
<wdnmd2></wdnmd2>
</div>
<script src="./vue.js"></script>
<script>
const test1 = Vue.extend({
template: "<h1>test1</h1>"
})
const test2 = Vue.extend({
template: "<div><h1>test2</h1><wdnmd1></wdnmd1></div>",
components: {
wdnmd1: test1
}
})
const app = new Vue({
el: "#app",
components: {
wdnmd2: test2
}
})
</script>
</body>
不需要直接实例化对象,直接在注册的时候进行实例化;{}
就是一个组件对象
<body>
<div id="app">
<wdnmd1></wdnmd1>
<wdnmd2></wdnmd2>
</div>
<script src="./vue.js"></script>
<script>
Vue.component("wdnmd1", {
template: "<h1>全局组件</h1>"
})
const app = new Vue({
el: "#app",
components: {
wdnmd2: {
template: "<h1>局部组件</h1>",
}
}
})
</script>
</body>
使用 <script>
或者 <template>
标签都可以做到组件模板抽离
<script>
需要在 type 属性里面加上 text/x-template
,两者都必须指定 id 属性才能使用
组件模板必须有一个根(root)
<body>
<div id="app">
<wdnmd1></wdnmd1>
<wdnmd2></wdnmd2>
</div>
<template id="wdnmd2">
<div>
<h1>template:组件模板抽离</h1>
</div>
</template>
<script src="./vue.js"></script>
<script type="text/x-template" id="wdnmd1">
<div>
<h1>script:组件模板抽离</h1>
</div>
</script>
<script>
const app = new Vue({
el: "#app",
components: {
wdnmd1: {
template: "#wdnmd1",
},
wdnmd2: {
template: "#wdnmd2"
}
}
})
</script>
</body>
vue 组件也是 vue 实例,可以使用 data
属性来存放数据
数据必须是函数
<body>
<div id="app">
<wdnmd></wdnmd>
</div>
<script>
const app = new Vue({
el: "#app",
components: {
wdnmd: {
template: "<h1>{{title}}</h1>",
data() {
return {
title: "这是标题"
}
},
}
}
})
</script>
</body>
props 支持数据类型有:
定义父组件数据,并且在 component 内指定子组件名(子组件名与标签名一样则填一个即可)
const app = new Vue({
el: "#app",
data: {
movies: ['a', 'b', 'c']
},
components: {
wdnmd
}
})
使用 template 标签创建模板,遍历子组件 props 的自定义变量名
<template id="wdnmd">
<ul>
<li v-for="(item, index) in cmovies">{{item}}</li>
</ul>
</template>
创建子组件,template 填入 template 标签设定的 id,props 填入自定义数据名
const wdnmd = {
template: "#wdnmd",
props: ["cmovies"]
}
html 标签中使用模板,并且用 v-bind 来指定子组件与父组件数据对应
<div id="app">
<wdnmd :cmovies="movies"></wdnmd>
</div>
可以指定数据的类型,不满足则报错
基础类型检查
const wdnmd = {
template: "#wdnmd",
props: {
cmovies: Array
}
}
多个类型限制
[type1, type2, ...]
const wdnmd = {
template: "#wdnmd",
props: {
cmovies: [String, Number]
}
}
必须提供数据
required: true
const wdnmd = {
template: "#wdnmd",
props: {
cmovies: {
type: Array,
required: true
}
}
}
如果没传入数据则显示设定的默认数据
default() {return ...}
const wdnmd = {
template: "#wdnmd",
props: {
cmovies: {
type: Array,
default() {
return ["真就没数据呗"]
}
}
}
}
如果子组件的变量使用驼峰命名,则在用 v-bind 指定时需要把驼峰位置使用符号 -
代替,并且将大写改成小写
<body>
<div id="app">
<!-- 子对象变量需要在驼峰位置使用 - 代替 -->
<wdnmd :c-movies="movies"></wdnmd>
</div>
<template id="wdnmd">
<ul>
<!-- 模板也支持子对象变量大写 -->
<li v-for="(item, index) in cMovies">{{item}}</li>
</ul>
</template>
<script>
const wdnmd = {
template: "#wdnmd",
// 子对象变量大写
props: ["cMovies"]
}
const app = new Vue({
el: "#app",
data: {
movies: ['a', 'b', 'c']
},
components: {
wdnmd
}
})
</script>
</body>
子组件中使用 $emit
来触发事件;父组件则使用 v-on
来监听事件
子组件定义需要传到父组件的数据,并定义方法
this.$emit('自定义事件名', 需要传入的参数)
const wdnmd = {
template: "#wdnmd",
data() {
return {
list: [
{id: "1", name: "家用电器"},
{id: "2", name: "日用护肤"},
{id: "3", name: "带你们打"},
]
}
},
methods: {
btn(item) {
this.$emit('itemclick', item)
}
},
}
父模板定义 v-on 监听事件,方法不需要填入括号
<div id="app">
<wdnmd @itemclick="itemclick"></wdnmd>
</div>
父组件定义方法
const app = new Vue({
el: "#app",
data: {
msg: ['a', 'b', 'c']
},
components: {
wdnmd
},
methods: {
itemclick(item) {
console.log(item.name);
}
}
})
$children(不常用)
在父组件下使用 this.$children
来获取子组件,可通过数组取下标来调用子组件方法
<body>
<div id="app">
<wdnmd></wdnmd>
<button @click="click()">点我</button>
</div>
<template id="wdnmd1">
<div>子组件</div>
</template>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
components: {
wdnmd: {
template: "#wdnmd1",
data() {
return {
name: "子组件name"
}
},
}
},
methods: {
click() {
console.log(this.$children[0].name);
}
},
})
</script>
</body>
$refs
在父组件下使用 $refs
来获取子组件,并且通过 ref
属性在父模板定义子组件的标签
指定名字后,可通过 this.$refs.ref自定义名.方法名
来获取子组件方法
<body>
<div id="app">
<wdnmd></wdnmd>
<wdnmd></wdnmd>
<wdnmd ref="wdnmd1"></wdnmd>
<button @click="click()">点我</button>
</div>
<template id="wdnmd1">
<div>子组件</div>
</template>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
components: {
wdnmd: {
template: "#wdnmd1",
data() {
return {
name: "子组件name"
}
},
}
},
methods: {
click() {
console.log(this.$refs.wdnmd1.name);
}
},
})
</script>
</body>
$parent
子组件通过 $parent
来访问父组件
<body>
<div id="app">
<wdnmd></wdnmd>
</div>
<template id="wdnmd">
<div>
<h1>我是子组件</h1>
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data() {
return {
name: "name"
}
},
components: {
wdnmd: {
template: "#wdnmd",
methods: {
btnClick() {
console.log(this.$parent.name);
}
},
}
}
})
</script>
</body>
$root
子组件通过 $root
来获取根组件
<body>
<div id="app">
<wdnmd></wdnmd>
</div>
<template id="wdnmd">
<div>
<h1>我是子组件1</h1>
<button @click="btnClick">按钮</button>
<wdnmd1></wdnmd1>
</div>
</template>
<template id="wdnmd1">
<div>
<h1>我是子组件2</h1>
<button @click="btnClick1">按钮</button>
</div>
</template>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data() {
return {
name: "name"
}
},
components: {
wdnmd: {
template: "#wdnmd",
methods: {
btnClick() {
console.log(this.$parent.name);
}
},
components: {
wdnmd1: {
template: "#wdnmd1",
methods: {
btnClick1() {
console.log(this.$root.name);
}
},
}
}
}
}
})
</script>
</body>
组件的插槽是为了让组件更具有拓展性,让使用者决定组件内部展示内容
模板内写入插槽标签 <slot></slot>
<template id="wdnmd">
<div>
<h1>我是子组件1</h1>
<button @click="btnClick">按钮</button>
<slot></slot>
</div>
</template>
调用在 slot 标签内填入内容
<div id="app">
<wdnmd><button>wdnmd</button></wdnmd>
<wdnmd></wdnmd>
<wdnmd></wdnmd>
</div>
插槽可以写入默认值,如果在调用时没有给 slot 内容那么会显示默认值
<template id="wdnmd">
<div>
<h1>我是子组件1</h1>
<button @click="btnClick">按钮</button>
<slot>这是slot默认值</slot>
</div>
</template>
slot 标签也可以填入多个标签,一起作为替换元素
<div id="app">
<wdnmd><button>wdnmd</button><button>wdnmd1</button></wdnmd>
<wdnmd></wdnmd>
<wdnmd></wdnmd>
</div>
当有多个 slot 插槽时就需要使用 name 属性来命名
<div id="app">
<wdnmd><button slot="slot1">按钮</button></wdnmd>
<wdnmd></wdnmd>
<wdnmd><b slot="slot3">加粗</b></wdnmd>
</div>
<template id="wdnmd">
<div>
<h1>我是子组件1</h1>
<button @click="btnClick">按钮</button>
<slot name="slot1"></slot>
<slot name="slot2"></slot>
<slot name="slot3"></slot>
</div>
</template>
当需要使用 slot 插槽并且调用数据的话,就需要用到
现在 data 中定义好数据,然后在模板的 slot 中使用属性 :data="数据名"
(data为自定义名) 来指定数据,最后调用 slot 标签中使用 slot-scope="slot"
(slot为自定义名)来调用数据,并且使用 slot.data
来取出数据
<body>
<div id="app">
<wdnmd></wdnmd>
<wdnmd>
<slot slot-scope="slot">
<span v-for="(item, index) in slot.data">{{item}} - </span>
</slot>
</wdnmd>
</div>
<template id="wdnmd">
<div>
<slot :data="text">
<ul>
<li v-for="(item, index) in text">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
components: {
wdnmd: {
template: "#wdnmd",
data() {
return {
text: ['1', '2', '3', '4', '5']
}
},
}
}
})
</script>
</body>
解决变量命名冲突问题
导入 js 时,给上 type 属性,并且填入 module
<script src="./aaa.js" type="module"></script>
<script src="./bbb.js" type="module"></script>
<script src="./ccc.js" type="module"></script>
使用 export 导出变量与方法
let name = '小明'
let age = 18
let flag = true
function sum(num1, num2) {
return num1 + num2
}
export {sum, flag}
在需要使用的地方使用 import 接收变量与方法
import {sum, flag} from './aaa.js'
console.log(sum(10, 20));
if(flag) {
console.log("aaa");
}
其他导出方式
// 导出变量
export let name = 'xiaohong'
export let age = 18
// 导出方法
export function num() {
return 'aaa'
}
// 导出类
export class list {
listA(name, age) {
this.name = name;
this.age = age;
}
}
他不允许本地直接路径调用,否则会出现跨域错误!需要搭建服务器访问解决
导出一个不需要命名,可以让使用者自己命名的模块
在同一个模块中只能存在一个 export default
导出
export default function(hello) {
console.log(hello);
}
调用时自定义名字
import wdnmd from './bbb.js'
wdnmd('hello');
使用通配符 * 来导入,并且使用 as 来指定一个名字,作为调用名使用
import * as aaa from './aaa.js'
if(aaa.flag) {
console.log("aaa flag");
}
npm install -g webpack
npm install -g @vue/cli
npm install -g @vue/cli-init
// CLI2 初始化
vue init webpack my-project
// CLI3 初始化
vue create my-project
CLI2
npm run dev
启动 vue 项目
无参无返
const test = () => {
console.log("wdnmd");
}
test()
有参有返
一个参数时可以省略括号
// 两个参数
const test = (sum1, sum2) => {
return sum1 + sum2
}
console.log(test(10, 20));
// 一个参数
const test2 = num => {
return num * num
}
console.log(test2(20));
代码块只有一行
可以省略掉方法体括号以及 return,自动将一行代码结果 return
const test3 = num => num * num
console.log(test3(20));
如果需要设置全局变量,在main.js中,Vue实例化的代码里添加。
Vue.prototype.$appName = ‘My App’
这样 $appName 就在所有的 Vue 实例中可用了,甚至在实例被创建之前就可以。如果我们运行:
new Vue({
beforeCreate: function () {
console.log(this.$appName)
}
})
则控制台会打印出 My App
路由决定了数据包来源到目的地的路径
将输入端的数据转到合适的输出端
安装
cnpm install vue-router --save
直接去路由映射寻找需要的组件,并返回前端显示
所有导向过程都会放入栈结构,而返回操作则是取出栈结构
向栈结构存入
location.hash = 'wdnmd'
向栈结构存入
history.pushState({}, '', 'wdnmd')
从栈结构取出,url 返回上一级
history.back()
从栈结构放回返回的东西,url 前进后一级
history.forward()
直接替换 url,不存入栈结构
history.replaceState({}, '', 'wdnmd')
直接跳转到某一栈的位置
// 基于当前位置返回栈 2 个位置
history.go(-1)
history.go(-2)
// 基于当前位置前进栈 2 个位置
history.go(1)
history.go(2)
// 配置路由相关信息
import Vue from 'vue'
import VueRouter from 'vue-router'
// 通过 Vue.use 来安装插件
Vue.use(VueRouter)
// 创建 VueRoutes 对象
const routes = [
]
const router = new VueRouter({
routes
})
// 将 router 对象传入到 Vue 实例中
export default router
在 main.js 中挂载
import Vue from 'vue'
import App from './App.vue'
// 挂载
import router from './router'
Vue.config.productionTip = false
new Vue({
// 引用
router,
render: h => h(App)
}).$mount('#app')
创建路由组件
<template>
<div>
<h1>我是hello</h1>
</div>
</template>
<script>
export default {
name: "hello"
}
</script>
配置路由映射
// 导入 vue 模块
import home from '../components/hello'
import about from '../components/about'
// 通过 Vue.use 来安装插件
Vue.use(VueRouter)
// 创建 VueRoutes 对象
const routes = [
// 配置 vue 模块映射配置
{
path: '/hello',
component: home
},
{
path: '/about',
component: about
}
]
通过 router-link 与 router-view 来显示
<template>
<div id="app">
<router-link to="/hello">hello</router-link>
<br />
<router-link to="/about">about</router-link>
<router-view></router-view>
</div>
</template>
router-link 最终会渲染成 a 标签,router-view 会根据当前路径渲染出不同的组件
网站一进入默认载入显示组件
path 设置为 / 默认网站根目录,然后通过 redirect 重定向到首页
const routes = [
//默认重定向
{
path: '/',
redirect: '/hello'
},
// 配置 vue 模块映射配置
{
path: '/hello',
component: home
},
{
path: '/about',
component: about
}
]
路由配置的 router 函数里面添加一项 mode
const router = new VueRouter({
routes,
mode: 'history'
})
tag:可以修改默认渲染标签
<router-link to="/hello" tag="button">hello</router-link>
<router-link to="/about" tag="button">about</router-link>
replace:使用 history.replaceState 来跳转组件
<router-link to="/hello" tag="button" replace>hello</router-link>
<router-link to="/about" tag="button" replace>about</router-link>
linkActiveClass:当前选择的组件 class 名字自定义
const router = new VueRouter({
routes,
mode: 'history',
linkActiveClass: 'active'
})
通过 this.$router.push
来实现
<template>
<div id="app">
<button @click="homeClick">home</button>
<button @click="aboutClick">about</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
homeClick() {
this.$router.push('/hello')
},
aboutClick() {
this.$router.push('/about')
}
},
}
</script>
<style>
</style>
访问路由映射的组件时传递一些数据
配置路由映射,添加指定参数名字
const routes = [
{
path: '',
redirect: '/hello'
},
{
path: '/hello',
component: home
},
{
// 添加参数名
path: '/about/:name',
component: about
}
]
跳转时需要带上参数
<router-link :to="'/about/' + name" tag="button">about</router-link>
通过 $route 取出当前活跃组件的数据
<template>
<div>
<h1>我是关于{{username}}</h1>
</div>
</template>
<script>
export default {
name: 'about',
computed: {
username() {
return this.$route.params.name
}
},
}
</script>
将对应的 js 打包成一个个的 js 块,只有在路由被访问时才加载响应组件
路由映射组件方式改为调用组件则映射
// 导入 vue 模块
const home = () => import('../components/hello')
const about = () => import('../components/about')
定义两个子组件,然后在路由中导入
const aboutA = () => import('../components/aboutA')
const aboutB = () => import('../components/aboutB')
使用 children
来配置嵌套路由映射
CLI3 中 嵌套路由映射的 path 不需要加上 /
const routes = [
{
path: '',
redirect: '/hello'
},
{
path: '/hello',
component: home
},
{
path: '/about',
component: about,
// 嵌套路由映射
children: [
{
path: 'aboutA',
component: aboutA
},
{
path: 'aboutB',
component: aboutB
}
]
}
]
然后需要在父组件中使用 router-link
与 router-view
来显示子组件
to 属性需要指定子组件绝对路径
<router-link to="/about/aboutA">aboutA</router-link>
<router-link to="/about/aboutB">aboutB</router-link>
<router-view></router-view>
当然,路由嵌套也可以实现默认重定向
const routes = [
{
path: '',
redirect: '/hello'
},
{
path: '/hello',
component: home
},
{
path: '/about',
component: about,
// 嵌套路由映射
children: [
// 默认重定向
{
path: '',
redirect: 'aboutA'
},
{
path: 'aboutA',
component: aboutA
},
{
path: 'aboutB',
component: aboutB
}
]
}
]
通过 query 传递参数
使用 v-bind 传递一个对象,对象包含 path 与 query
<template>
<div id="app">
<router-link to="/hello" tag="button">hello</router-link>
<router-link to="/about" tag="button">about</router-link>
<router-link :to="user" tag="button">user</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
user: {
path: '/user',
query: {
name: 'xiaoming',
age: 18,
height: 188
}
}
}
},
}
</script>
对应模块中使用 $route.query
来调用参数
<template>
<div>
<h1>user</h1>
<p>{{$route.query.name}}</p>
<p>{{$route.query.age}}</p>
<p>{{$route.query.height}}</p>
</div>
</template>
代码实现参数传递
<template>
<div id="app">
<button @click="userClick">buttonClickUser</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
userClick() {
this.$router.push({
path: '/user',
query: {
name: 'xiaohong',
age: 88,
height: 22
}
})
}
},
}
</script>
<style>
</style>
使用 router.beforeEach
检测组件创建,修改 document 标题
使用 meta 设置每个组件的 title 变量
const routes = [
{
path: '',
redirect: '/hello'
},
{
path: '/hello',
component: home,
meta: {
title: '首页'
}
},
{
path: '/about',
component: about,
meta: {
title: '关于'
},
children: [{
path: '',
redirect: 'aboutA'
},
{
path: 'aboutA',
component: aboutA
},
{
path: 'aboutB',
component: aboutB
}
]
},
{
path: '/user',
component: user,
meta: {
title: 'user'
}
}
]
router.beforeEach 监听组件激活
router.beforeEach((to, from, next) => {
document.title = to.matched[0].meta.title
next()
})
to
:即将进入的目标路由对象from
:当前导航正要离开的路由next()
:进入下一个钩子
next(false)
:中断导航,如果 url 改变则重置到 from 路由对应地址next('/')
或者next({ path: '/'})
:跳转到不同的地址,中断当前导航并进行新的导航。next(error)
:(2.4.0+) 如果传入 next
的参数是一个 Error
实例,则导航会被终止且该错误会被传递给 router.onError()
注册过的回调。router.afterEach((to, from) => {
console.log("afterEach");
})
beforeRouteEnter
:组件加载前beforeRouteUpdate
:当路由改变,组件复用时调用(/user/:id 中来回调用 /user/1 + /user/2 时调用)beforeRouteLeave
:导航离开该组件前调用阻止组件被销毁,与频繁的创建
将 router-view
标签使用 keep-alive
包裹住即可
<keep-alive>
<router-view />
</keep-alive>
使用这个标签后即可使用 activated
与 deactivated
两个函数
activated
:创建组件时deactivated
:销毁组件后router-view
如果直接被包含在 keep-alive
,则所有组件都会被缓存
选择多个组件用 ,
分割,不能添加空格
<keep-alive include="user,about">
<router-view />
</keep-alive>
resolve
(成功后回调函数) 与 reject
(失败后回调函数)then
方法,失败则进入 catch
方法异步操作使用 Promise ,拿到结果后通过 resolve
来进行下一步的 then 处理;如果没拿到结果则调用 reject
来进行下一步的 catch 处理
new Promise((resolve, reject) => {
let data = 1
resolve(data)
}).then((data) => {
console.log(data++);
return new Promise((resolve, reject) => {
resolve(data)
})
}).then((data) => {
console.log(data++);
return new Promise((resolve, reject) => {
resolve(data)
})
}).then((data) => {
console.log(data++);
return new Promise((resolve, reject) => {
reject("error")
})
}).catch((err) => {
console.log(err);
})
then 与 catch 的另一种写法
new Promise((resolve, reject) => {
// resolve("1")
reject("error")
}).then((data) => {
console.log(data);
}, (error) => {
console.log(error);
})
多个请求的使用
Promise.all([
new Promise((resolve, reject) => {
resolve("1")
}),
new Promise((resolve, reject) => {
resolve("2")
})
]).then((results) => {
console.log(results);
})
全局可获取或者修改的变量,支持响应式
安装
cnpm install vuex --save
src 目录下新建目录 store,并且创建 index.js 文件
import Vue from 'vue'
import Vuex from 'vuex'
// 安装插件
Vue.use(Vuex)
// 创建对象
const store = new Vuex.Store({
})
// 导出对象
export default store
main.js 引入 store 对象
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
store,
router,
render: h => h(App)
}).$mount('#app')
store 对象添加 state 属性,并且写入数据
// 创建对象
const store = new Vuex.Store({
state: {
num: 100
}
})
组件调用
<h1>{{$store.state.num}}</h1>
在 mutations
属性下定义需要的方法
// 创建对象
const store = new Vuex.Store({
state: {
num: 100
},
mutations: {
jia(state) {
state.num++
},
jian(state) {
state.num--
}
},
})
使用 $store.commit
调用 store 方法
export default {
name: 'app',
methods: {
jia() {
this.$store.commit('jia')
},
jian() {
this.$store.commit('jian')
}
},
}
使用对象传递,type 为 mutations 中的方法名;使用 payload 接收
export default {
name: 'app',
methods: {
addStu() {
this.$store.commit({
type: 'addStu',
a: {
name: 'xiaohong',
age: 20
}
})
}
},
}
const store = new Vuex.Store({
state: {
num: 100,
student: [
{
name: 'xiaoming',
age: 18
},
]
},
mutations: {
addStu(state, payload) {
state.student.push(payload)
}
}
})
store 目录下新建类型常量文件
并且将需要定义为常量的方法导出
export const jia = 'jia'
需要调用方法的时候接收一下
import {
jia
} from './mutations'
方法实现
const store = new Vuex.Store({
state: {
num: 100,
student: [
{
name: 'xiaoming',
age: 18
},
]
},
mutations: {
[jia](state) {
state.num++
}
}
})
方法调用
import {
jia
} from './store/mutations'
export default {
name: 'app',
methods: {
jia() {
this.$store.commit(jia)
}
}
}
store 目录下 index.js 写入 actions 对象
const store = new Vuex.Store({
state: {
student: [
{
name: 'xiaoming',
age: 18
},
]
},
mutations: {
// 修改
replace(state) {
state.student[0].name = "wdnmd"
}
},
actions: {
// 实现异步操作
replacee(context) {
setTimeout(() => {
context.commit('replace')
}, 1000);
}
}
})
调用 action 方法
export default {
name: 'app',
methods: {
replace() {
this.$store.dispatch('replacee')
}
},
}
当一个数据需要经过一系列操作后得到的
// 创建对象
const store = new Vuex.Store({
state: {
num: 100
},
getters: {
// 直接返回 state 数据
cheng(state) {
return state.num * state.num
},
// 返回 getters 其他方法的数据
cheng1(getters) {
return getters.cheng
},
// 返回有参数据
cheng2(state) {
return a => {
return state.num + a
}
}
}
})
<h2>{{$store.getters.getText}}</h2>
Vue 属于单一状态树,许多状态会交给 Vuex 来管理;应用过于复杂时 story 则非常臃肿。
此时就可以使用 Module
抽离出来新的模块方便管理
module 的对象写法必须写在 story 对象前面才能运行!否则会出现 初始化前无法访问 的报错
const moduleA = {
state: {
text: 'aModule'
}
}
const moduleB = {
state: {
text: 'bModule'
}
}
const store = new Vuex.Store({
state: {
user: [
{
name: 'xiaoming',
age: 18
}
]
},
modules: {
a: moduleA,
b: moduleB
}
})
命名不允许冲突
在子模块的方法之类的是不允许与 story
对象的方法名字冲突
如果当 commit
调用方法时,会先去 store
对象寻找,未找到则会去子模块寻找
所以命名不会出现冲突
特点
安装
cnpm install axios --save
main.js 导入
import axiox from 'axios'
请求 api 并 log 输出
let serverInterface = 'api地址'
axiox({
url: serverInterface
}).then(res => {
console.log(res);
})
全局配置
每一个组件可通过 $http
来发起 axios 请求
Vue.prototype.$http = axios
get 请求参数拼接
axiox({
url: serverInterface,
params: {
type: 'test'
}
}).then(res => {
console.log(res);
})
当多个请求有结果执行 then
axiox.all([
axiox({
url: serverInterface
}),
axiox({
url: serverInterface
})
]).then(a => {
console.log(a[0]);
console.log(a[1]);
})
url:'/user'
:请求地址method:'get'
:请求类型baseURL:''
:请求根路径transformRequest:[fuction(data){}]
:请求前的数据处理transformResponse:[fuction(data){}]
:请求前的数据处理headers:{'x-Requested-With':'XMLHttpRequest'}
:自定义请求头params:{id:12}
:URL 查询对象paramsSerializer:fuction(params){}
:查询对象序列化函数data:{key:'aa'}
:request bodytimeout:1000
:超时设置withCredentials:false
:跨域是否带 tokenadapter:fuction(resolve, reject, config){}
:自定义请求处理auth:{uname:'', pwd:'12'}
:身份验证信息responseType:'json'
:响应的数据格式,支持 / json / blob / document / arraybuffer / text / stream在 src 中 network 文件夹的 request.js 文件进行封装配置
import axios from 'axios'
export function request(config) {
const test = axios.create({
baseURL: 'https://gitee.com/api/v5/repos/nutssss/cdn/issues?access_token=0d4656bf06d57b8da4cee1fadba777e6&state=open&sort=created&direction=desc&page=1&per_page=20',
timeout: 5000
})
return test(config)
}
导入 request,进行调用使用
import {request} from './network/request'
request({
url: ''
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
拦截掉当前请求,处理后需要 return 请求进行后续其他处理
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
API 要求:需要授权的 API ,必须在请求头中使用 Authorization 字段提供 token 令牌
// 拦截请求,在头部增加 token 信息
initialize.interceptors.request.use(res => {
// 获取 token
res.headers.Authorization = window.sessionStorage.getItem('token')
return res;
})
import axios from 'axios'
export function request(config) {
// 创建 axios 实例
const test = axios.create({
baseURL: 'url',
timeout: 5000
})
// 请求拦截器
test.interceptors.request.use(res => {
console.log(res);
return res
}, err => {
console.log(err);
})
// 响应拦截器
test.interceptors.response.use(res => {
return res.data;
}, err => {
console.log(err);
})
// 发送网络请求
return test(config)
}
src 根目录新建配置文件 vue.config.js
const path = require('path');
function resolve(dir) {
return path.join(__dirname, dir);
}
module.exports = {
lintOnSave: true,
chainWebpack: (config) => {
config.resolve.alias
.set('@', resolve('src'))
.set('assets',resolve('src/assets'))
.set('components',resolve('src/components'))
.set('network',resolve('src/network'))
.set('views',resolve('src/views'))
}
};
html 标签内引用需要加上 ~
号,JavaScript 里面引用则不需要
存储
window.sessionStorage.setItem('token', tokenString);
取出
window.sessionStorage.getItem('token');
清空
window.sessionStorage.clear()
防止越权访问
// 全局路由防止越权访问
router.beforeEach((to, from, next) => {
// 访问 login 登陆则放行
if(to.path === "/login") return next();
// 判断是否存在 token
if(window.sessionStorage.getItem('token')) {
console.log("存在token 放行");
return next()
} else {
console.log("不存在token,爬");
return next("/login")
}
})
fileUpload() {
// 获取文件信息
let faceFile = document.querySelector(".inputFile").files[0];
// 创建 form 表单
let formData = new FormData();
// 文件写入
formData.append("faceFile", faceFile, faceFile.name);
// 设置头部
let config = {
headers: {
'Content-Type': 'multipart/form-data',
'headerUserToken': this.$stor.session.get("userInfo").token
}
};
// 请求发送
this.$http.post(this.API.uploadFace + "/" + this.$stor.session.get("userInfo").userId, formData, config).then(res => {
if (res.status == 200) {
Toast({
message: '修改成功'
});
}
}, err => {
console.log(err);
})
}
export default {
data () {
return {
// 滚动条的高度
scrollTop: 0
}
},
methods: {
// 保存滚动值,这是兼容的写法
handleScroll () {
this.scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
},
// 滚动条回到顶部
backTop () {
if (this.scrollTop > 10) {
document.documentElement.scrollTop = 0
}
}
},
mounted () {
window.addEventListener('scroll', this.handleScroll, true)
},
destroyed () {
// 离开该页面需要移除这个监听的事件,不然会报错
window.removeEventListener('scroll', this.handleScroll)
}
}
图标库
npm 安装
npm install font-awesome --save
main.js 引入
import "font-awesome/css/font-awesome.min.css";
使用即可
动画重复播放
npm 安装
npm install scrollreveal --save
封装
// 重复动画
import scrollReveal from 'scrollreveal';
// 启用动画方法
export default {
Reset: (dom) => {
let a = scrollReveal();
dom.forEach(item => {
a.reveal(item, {
// 动画的时长
duration: 500,
// 延迟时间
delay: 200,
// 动画开始的位置,'bottom', 'left', 'top', 'right'
origin: "bottom",
// 回滚的时候是否再次触发动画
reset: true,
// 在移动端是否使用动画
mobile: false,
// 滚动的距离,单位可以用%,rem等
distance: "0",
// 其他可用的动画效果
opacity: 0.001,
easing: "cubic-bezier(.06,-0.14,.42,1.5)",
scale: 0.8,
});
});
},
NoReset: (dom) => {
let a = scrollReveal();
dom.forEach(item => {
a.reveal(item, {
// 动画的时长
duration: 500,
// 延迟时间
delay: 300,
// 动画开始的位置,'bottom', 'left', 'top', 'right'
origin: "bottom",
// 在移动端是否使用动画
mobile: false,
// 滚动的距离,单位可以用%,rem等
distance: "0",
// 其他可用的动画效果
opacity: 0.001,
easing: "cubic-bezier(.06,-0.14,.42,1.5)",
scale: 0.8,
});
});
},
}
main.js 引入
// 重复动画
import scrollReveal from './assets/js/scrollReveal';
Vue.prototype.scrollAni = scrollReveal;
使用
// 动画
this.scrollAni.NoReset([".haha", ".TitleBox", ".footer"]);
图片懒加载
[vue-lazyload - npm (npmjs.com)](https://www.npmjs.com/package/vue-lazyload)
npm 安装
npm install vue-lazyload --save-dev
main.js 引入
// 图片懒加载
import VueLazyload from 'vue-lazyload';
Vue.use(VueLazyload, {
preLoad: 1.3,
loading: require("./assets/images/loading.gif"),
error: require('./assets/images/error.jpg'),
attempt: 1
});
Vue.use(VueLazyload);
使用
<img v-lazy="img" :key="img" />
富文本编辑器
Introduction · wangEditor 用户文档
npm 安装
npm i wangeditor --save
main.js 引入
// 富文本编辑器
import E from 'wangeditor'
Vue.prototype.E = E;
使用
// 富文本配置
createEditor() {
// 媒体上传oos配置
// region以杭州为例(oss-cn-hangzhou),其他region按实际情况填写。
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
let client = new this.oos({
region: "xxx",
accessKeyId: "xxx",
accessKeySecret: "xxx",
bucket: "tucao-haha",
});
// 富文本编辑器启用
let ed = new this.E(".edMenu", ".ed");
// 菜单配置
ed.config.menus = [
"head",
"bold",
// "fontSize",
// "fontName",
"italic",
"underline",
"strikeThrough",
// "indent",
// "lineHeight",
// "foreColor",
//"backColor",
"link",
"list",
// "todo",
"justify",
"quote",
// "emoticon",
"image",
"video",
"table",
// "code",
"splitLine",
"undo",
"redo",
];
// 关闭粘贴样式
ed.config.pasteFilterStyle = false;
// 代码高亮
ed.highlight = this.hljs;
// 提示文本
ed.config.placeholder = "开始您的吐槽吧!";
// 图片上传
// resultFiles 是 input 中选中的文件列表
// insertImgFn 是获取图片 url 后,插入到编辑器的方法
ed.config.customUploadImg = (resultFiles, insertImgFn) => {
let vm = this;
vm.loadStart();
resultFiles.forEach(item => {
let info = item.name.split(".");
client
.put(`${info[0]}-${Date.parse(new Date())}.${info[1]}`, item)
.then(function (res) {
// 上传图片,返回结果,将图片插入到编辑器中
insertImgFn(res.url);
vm.imgBack();
})
.catch(function (err) {
console.log(err);
});
});
};
// 视频上传
// resultFiles 是 input 中选中的文件列表
// insertVideoFn 是获取视频 url 后,插入到编辑器的方法
ed.config.customUploadVideo = (resultFiles, insertVideoFn) => {
let vm = this;
vm.loadStart();
let info = resultFiles[0].name.split(".");
client
.put(`${info[0]}-${Date.parse(new Date())}.${info[1]}`, resultFiles[0])
.then(function (res) {
// 上传视频,返回结果,将视频插入到编辑器中
insertVideoFn(res.url);
vm.videoBack();
})
.catch(function (err) {
console.log(err);
});
};
// 图片上传大小
ed.config.uploadImgMaxSize = 5 * 1024 * 1024;
// 图片上传类型
ed.config.uploadImgAccept = ["jpg", "jpeg", "png", "gif", "bmp"];
// 一次最多上传 5 个图片
ed.config.uploadImgMaxLength = 5;
// 配置 onchange 回调函数
ed.config.onchange = (content) => {
this.article.articleContent = content;
}
// 创建
ed.create();
// 默认样式修改
document.querySelector(".w-e-toolbar").style.background = "transparent";
document.querySelector(".w-e-toolbar").style.borderWidth = "0";
document.querySelector(".w-e-text-container").style.background =
"transparent";
document.querySelector(".w-e-text-container").style.border =
"transparent";
// 赋值内容
ed.txt.html(this.article.articleContent);
},
yarn add viewerjs
if (!this.Viewer) {
return (this.Viewer = new Viewer(document.querySelector("#content")));
}
this.Viewer.update();
格式化工具
步骤
"pre": "prettier --write ."
useTabs | 使用tab缩进还是空格 |
---|---|
tabWidth | tab是空格情况下是几个空格 |
printWidth | 当行字符的长度,推荐80 |
singleQuote | 使用单引号还是双引号 true为单 |
trailingComma | 在多行输入的尾逗号是否添加 |
semi | 语句末尾是否要加分号,默认true |
{
"useTabs": false,
"semi": true,
"singleQuote": false,
"bracketSpacing": true,
"trailingComma": "none",
"tabWidth": 4,
"printWidth": 80
}
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
.public/*
解决eslint和prettierrc冲突
npm i eslint-plugin-prettier eslint-config-prettier -D
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
vue.config.js 里面配置
module.exports = {
// 修改或新增html-webpack-plugin的值,在index.html里面能读取htmlWebpackPlugin.options.title
chainWebpack: config =>{
config.plugin('html')
.tap(args => {
args[0].title = "宝贝商城";
return args;
})
}
};
然后修改掉 dist 中 index.html 的 title 为
<title><%= htmlWebpackPlugin.options.title %></title>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。