1. 安装
全局安装webpack,全局安装Vue脚手架
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install webpack -g
cnpm install vue-cli -g
2. 创建项目
// 2.0 vue-cli
vue init webpack [name]
npm run dev
// 3.0 vue-cli
vue create [name]
npm run serve
// 在终端打开命令
vue ui
3. v-html 数据绑定
模板语法:{{ }}
v-html:可以解析标签
v-text:解析为纯文本
new Vue({
el: '#app',
data: {
name: 'Vue',
isBool: true
}
})
<h2>模板语法</h2>
<h4>{{name}}</h4>
<h4>hello,{{name}}</h4>
<h4>{{'hello,'+name}}</h4>
<!-- v-html -->
<h2>v-html</h2>
<h4 v-html="'hello,'+name"></h4>
<h4 v-html="isBool?'success':'error'"></h4>
<h4 v-html="Math.random()"></h4><br>
<!-- v-text -->
<h2>v-text</h2>
<h4 v-text="'hello,'+name"></h4>
<h4 v-text="isBool?'success':'error'"></h4>
<h4 v-text="Math.random()"></h4><br>
4. v-bind 属性绑定
v-bind 绑定属性,可以写成 " : "(冒号)
new Vue({
el: '#app',
data: {
link: './img/bg01.jfif',
web: {
link: './img/bg02.jfif',
src: './2-数据绑定.html',
title: '数据绑定'
}
}
})
<div id="app">
<img v-bind:src="link" alt=""><br>
<a v-bind:href="web.src">
<img v-bind:src="web.link" alt="" v-bind:title="web.title">
</a>
</div>
5. v-model 表单数据绑定
- 输入框直接 v-model 获取输入内容
- 单选框获取 value 的值
- 下拉菜单:select 绑定v-model,获取 option 的value值
- 文本域直接获取 v-model 的值
- 多选框:获取 value 的多个值,用数组接收
// 实例化Vue
const vue = new Vue({
el: '#app',
data: {
user: {
name: "",
pass: "",
sex: "",
job: "",
mess: "",
hobby: [],
agree: false
},
},
methods: {
submit: function() {
for (var key in this.user) {
console.log(this.user[key])
}
}
},
})
<div id="app">
<!-- 输入框 -->
<div>账号:<input type="text" name="name" v-model="user.name"></div>
<div>密码:<input type="text" name="pass" v-model="user.pass"></div>
<!-- 单选框 -->
<div>性别:
<input type="radio" name="sex" v-model="user.sex" value="0-男">男
<input type="radio" name="sex" v-model="user.sex" value="1-女">女
</div>
<!-- 下拉框 -->
<div>职业:
<select name="job" v-model="user.job">
<option value="">--选择--</option>
<option value="web">web工程师</option>
<option value="python">python工程师</option>
<option value="C++">C++工程师</option>
</select>
</div>
<!-- 文本域 -->
<div>留言:
<textarea name="mess" v-model="user.mess" id="" cols="30" rows="10"></textarea>
</div>
<!-- 复选框 -->
<div>爱好:
<input type="checkbox" name="book" v-model="user.hobby" value="book">读书
<input type="checkbox" name="game" v-model="user.hobby" value="game">打游戏
<input type="checkbox" name="code" v-model="user.hobby" value="code">敲代码
</div>
<!-- 同意 -->
<div>
<input type="checkbox" v-model="user.agree" value="agree" name="" id="">同意
</div>
<!-- 提交按钮 -->
<button v-on:click="submit">提交</button>
</div>
6. v-if 条件渲染
- v-if:当条件为真时渲染,否则渲染 v-else,不渲染时删除节点
- v-show:不渲染时 display: none;
const vue = new Vue({
el: '#app',
data: {
isShow: true
}
})
<div id="app">
<!-- v-if -->
<p v-if="isShow">hello</p>
<P v-else>world</P>
<!-- v-show -->
<p v-show="isShow">hello</p>
</div>
7. v-for 列表渲染
// 实例化Vue
const vue = new Vue({
el: '#app',
data: {
imgArr: [
"./img/bg01.jfif",
"./img/bg02.jfif",
"./img/bg03.jfif",
"./img/bg04.jfif",
"./img/bg05.jfif",
"./img/bg06.jfif",
"./img/bg07.jfif",
"./img/bg08.jfif"
]
}
})
<div id="app">
<img v-for="item in imgArr" v-bind:src="item" alt="">
</div>
8. 动态样式
.red{
background: red;
}
.blue{
background: blue;
}
const vue = new Vue({
el: '#app',
data: {
isShow: true
}
})
<div id="app">
<p v-bind:class="[isShow ? 'red' : 'blue']">hello</p>
<P v-bind:class="{'red':true, 'blue':false}">world</P>
</div>
9. v-on - 事件绑定
1. 基本使用
v-on 事件绑定,可以写成 “ @ ”
const vue = new Vue({
el: '#app',
data: {
num: 10
},
methods: {
sub: function () {
this.num--;
},
add() {
this.num++;
}
}
})
<div id="app">
<h1>计数器</h1>
<h2>num:{{num}}</h2>
<button v-on:click="sub">--</button>
<button @click="add">++</button>
</div>
2. 事件修饰符
.prevent:阻止默认事件
.stop:阻止事件冒泡
.capture:事件捕获
.once:一次性事件
.self:只触发本事事件
.native:自定义组件绑定事件时 (在 router-link 上绑定事件时使用)
<div class="box" v-on:contextmenu.prevent="click('取消默认事件')">默认事件</div>
<div class="box" v-on:click.stop="click('阻止事件冒泡')">事件冒泡</div>
<div class="box" v-on:click.capture="click('事件捕获')">事件捕获</div>
<div class="box" v-on:click.once="click('一次性事件')">一次性事件</div>
<div class="box" v-on:click.self="click('事件本事')">事件本事</div>
@scroll.passive:滚动事件
<div @scroll.passive="scroll">...</div>
@click.left:鼠标左键
@click.right:鼠标右键
@click.middle:鼠标中键
<button @click.left>鼠标左键按下</button>
<button @click.right>鼠标右键按下</button>
<button @click.middle>鼠标中键按下</button>
.enter:回车键按下
.top .down .left .right
<input type="text" @keydown.enter="keydown('回车键按下')">
<input type="text" @keydown.up="keydown('up键按下')">
<input type="text" @keydown.down="keydown('down键按下')">
<input type="text" @keydown.left="keydown('left键按下')">
<input type="text" @keydown.right="keydown('right键按下')">
3. 表单修饰符
.lazy:表单失去焦点时响应
.trim:表单输入内容有空格时,失去焦点自动清除空格
<input type="text" v-model.lazy="name">
<input type="text" v-model.trim="name">
10. JSONP
-
创建
<script>
标签 -
给
<script>
添加 src 属性 -
插入到页面
var url = "接口地址" var os = document.createElement('script'); os.src = url; document.body.appendChild(os);
11. watch 监听属性
当监听的属性发生改变时,触发 watch 里的函数
<div id="app">
<input type="text" v-model="name" />
</div>
监听的属性不需要this,使用function函数
new Vue({
el: '#app',
data: {
name: ""
},
watch: {
name: function(){
console.log("namg改变了");
}
}
})
基本数据类型 简单监听
复合数据类型 深度监听
watch: {
name: {
handler(newd, oldd){
console.log("深度监听");
},
immediate: true, // 立即监听
deep: true // 是否深度监听,默认false
}
}
12. computed 计算属性
计算属性不能初始化,根据其他属性计算得到
计算属性已函数的形式定义
new Vue({
el: '#app',
data: {
arr: [1,2,3,4,5]
},
computed:{
// 获取
resArr(){
return arr.map(val=>{
return val*10;
})
},
// 获取和设置
title: {
get: function(){
return this.arr[0] + "-title"
},
set: function(str){
this.arr[1] = "001" + str
}
}
}
})
<div id="app">
<ul>
<li v-for="item in resArr">{{item}}</li>
</ul>
</div>
13. 服务器返回数据的问题
服务器返回的数据中,当修改了某个属性时,可能不会引起页面的自动渲染,解决办法:
1. 取出数据
2. 修改数据
3. 重新赋值
14. filter 过滤器
// 实例化Vue
const vue = new Vue({
el: '#app',
data: {
arr: [
{id:1, name:"html", price:180, num:100},
{id:2, name:"vue", price:200, num:50},
{id:3, name:"node", price:240, num:10},
],
},
methods: {},
watch: {},
computed: {},
// 局部定义过滤器
filters: {
// 每本原价减 20
filPrice(val) {
return val-20;
}
}
})
<div id="app">
<h1>局部filter</h1>
<ul>
<li>
<h4>名字</h4>
<h4>原价</h4>
<h4>现价(filter)</h4>
<h4>数量</h4>
</li>
<li v-for="item in arr" :key="item.id">
<span>《{{item.name}}》</span>
<span><del>{{item.price}}</del>元</span>
<span>{{item.price | filPrice}}元</span>
<span>{{item.num}}本</span>
</li>
</ul>
</div>
全局过滤器
filter / index.js
const timerFil = (val)=>{
let date = new Date(val);
let year = date.getFullYear(); // 年
let month = (date.getMonth()+'').padStart(2,'0'); // 月
let day = (date.getDate()+'').padStart(2,'0'); // 日
let hour = (date.getHours()+'').padStart(2,'0'); // 时
let min = (date.getMinutes()+'').padStart(2,'0'); // 分
let second = (date.getSeconds()+'').padStart(2,'0'); // 秒
return `${year}-${month}-${day} ${hour}:${min}:${second}`;
}
export default{
timerFil
}
main.js
import filter from './filter/index'
// 注册全局过滤器
for(let i in filter){
Vue.filter(i, filter[i])
}
index.vue
<p>时间:{{timer | timerFil}}</p>
15. <transition> 动画
<style> 在 module
模式下无效
<!-- appear:进场动画不生效时使用 -->
<transition name="fade" appear>
<div v-if="show">
<h4 class="message_title">{{ title }}</h4>
</div>
</transition>
/* 动画必须写在样式下面,否则不生效 */
/*---------- 动画 ----------*/
/* 动画开始 */
.flash-enter,.flash-leave-to{
width: 0px;
height: 0px;
}
/* 运行时间 */
.flash-enter-active,.flash-leave-active{
transition: all 600ms;
}
/* 动画结束 */
.flash-enter-to,.flash-leave{
width: 100px;
height: 100px;
}
15.1 <transition> Vue3
**使用 v-if
**
<style> 在 module
模式下无效
<!-- appear:进场动画不生效时使用 -->
<transition name="fade" appear>
<div v-if="show" style="top: -16px; left: -48px;">
<h4 class="message_title">{{ title }}</h4>
</div>
</transition>
/* 动画必须写在样式下面,否则不生效 */
/* 动画开始 */
.icon-msg-enter-from,
.icon-msg-leave-to{ top: 0; left: 0; }
/* 运行 */
.icon-msg-enter-active,
.icon-msg-leave-active{ transition: all 600ms linear; }
/* 动画结束 */
/* 标签上加了样式后可以不写结束样式 */
.icon-msg-enter-to,
.icon-msg-leave-from{ top: -16px; left: -48px; }
动画库:animate.css
<div id="app">
<transition
enter-active-class="animate [动画名称]"
leave-active-class="animate [动画名称]">
<div class="box" v-show="isShow"></div>
</transition>
</div>
16. *****生命周期*****
const vue=new Vue({
el: '#app',
data: {
num: "",
},
methods: {},
watch: {},
computed: {},
/*
创建实例开始
实例初始化之后,数据(data)不可用
*/
beforeCreate() {
console.log("--创建实例开始--");
console.log("el:",this.$el);
console.log("data:",this.$data);
},
/*
创建实例完成
实例创建完成,数据(data)可以使用,$el未挂载,不可用
*/
created() {
console.log("--创建实例完成--");
console.log("el:",this.$el);
console.log("data:",this.$data);
},
/*
挂载节点开始
*/
beforeMount(){
console.log("--挂载节点开始--");
console.log("el:",this.$el);
console.log("data:",this.$data);
},
// 挂载节点完成 (常用)
mounted(){
console.log("--挂载节点完成--");
console.log("el:",this.$el);
console.log("data:",this.$data);
},
// 更新节点开始
beforeUpdate(){
console.log("--更新节点开始--");
console.log("el:",this.$el);
console.log("data:",this.$data);
},
// 更新节点完成
updated() {
console.log("--更新节点完成--");
console.log("el:",this.$el);
console.log("data:",this.$data);
},
// 销毁实例前 (常用)
beforeDestroy(){
console.log("--销毁实例--");
console.log("el:",this.$el);
console.log("data:",this.$data);
},
// 销毁实例完成
destroyed(){
console.log("--销毁实例完成--");
console.log("el:",this.$el);
console.log("data:",this.$data);
}
})
在使用 <keep-alive>
时,组件将被缓存,被缓存的组件无法触发 mounted
生命周期
// 缓存的组件激活时
activated(){
console.log("缓存组件开始使用");
}
// 缓存组件停用时
deactivated(){
console.log("缓存组件结束使用");
}
17. ref 获取节点
<div id="app">
<p ref="text">hello world</p>
</div>
const vue=new Vue({
el: '#app',
data: {
num: "",
},
methods: {},
watch: {},
computed: {},
// 挂载节点完成
mounted(){
var result = this.$refs.text.innerHTML;
console.log(result);
},
})
17-1. ref使用
ref不能和 v-if 一起使用,v-show可以
18. vue-cli 脚手架
项目目录
-src
-assets //静态资源
-css
reset.css
-js
rem.js
-pages //路由组件
login.vue
index.vue
-common //公共组件
-components //页面组件
-filter //过滤器
index.js
-router //路由
index.js
-util //工具目录
request.js //数据请求
App.vue
main.js
19. $isServer 是否运行在服务器上
// 当前 Vue 实例是否运行于服务器
created(){
this.$isServer;
}
// 挂载实例 (到 id=app)
new Vue({}).$mount('#app');
20. slot 插槽
**单插槽:**使用<slot>
标签实现插槽
<!-- v-main组件 -->
<template>
<h1>单插槽</h1>
<slot></slot>
</template>
<template>
<v-main>
<p>这里是插槽内容</p>
</v-main>
</template>
**多插槽:**通过给<slot>
标签添加name
属性,定义多个插槽
<!-- v-main组件 -->
<template>
<h2>one</h2>
<slot name="one"></slot>
<h2>two</h2>
<slot name="two"></slot>
</template>
<template>
<v-main>
<p slot="one">这里是插槽 1 内容</p>
<p slot="two">这里是插槽 2 内容</p>
</v-main>
</template>
Vue3 插槽
<div>
<slot></slot>
</div>
<!-- 插槽默认名称default -->
<template v-slot:default>
<span>123</span>
</template>
插槽传值
<div>
<slot value="123"></slot>
</div>
<!-- 插槽默认名称default -->
<template v-slot:default="value">
<span>value</span>
</template>
21. 父子组件通信
父传子或者子传父都是父组件定义,子组件接收
1. 父 --> 子
-
通过给子组件绑定一个自定义属性,属性绑定一个值
-
子组件通过
props
数组接收
<!-- 父组件 -->
<template>
<h2>父组件</h2>
<v-child v-bind:msg="message"></v-child>
</template>
<script>
export default{
data(){
return {
message: "hello Vue"
}
}
}
</script>
<!-- 子组件 -->
<template>
<h2>子组件</h2>
<p>{{ msg }}</p>
</template>
<script>
export default{
props: {
msg: {
type: String,
default: "hello Vue",
required: true, // 必传
}
}
}
</script>
2. 子 --> 父
- 子组件 定义一个自定义事件
- 通过
this.$emit
绑定 - 父组件 接收自定义事件
<!-- 子组件 -->
<template>
<h2>子组件</h2>
<button v-on:click="cancel">自定义事件</button>
</template>
<script>
export default{
methods:{
cancel(){
this.$emit('cancel')
}
}
}
</script>
<!-- 父组件 -->
<template>
<h2>父组件</h2>
<v-child v-on:cancel="cancel"></v-child>
<p v-show="isShow">hello Vue</p>
</template>
<script>
export default{
data(){
return {
isShow: false
}
}
methods:{
cancel(){
this.isShow = !this.isShow;
console.log("子组件点击了");
}
}
}
</script>
22. vue-router 路由
1. 安装
npm install vue-router --save
2. 路由规则
当一级路由有二级路由时,一级路由不加name属性
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
import index from '../pages/index.vue'
export default new Router({
routes: [
// 首页
{
path: '/index',
component: index,
},
// 重定向
{
path: '*',
component: index
}
]
})
vue3.0 + vue-router
npm install vue-router@4 -D
import { createRouter, createWebHistory } from 'vue-router'
const routerHistory = createWebHistory()
const index = ()=>import('../components/index.vue')
const router = createRouter({
history: routerHistory,
routes: [
{
path: '/',
component: index
},
{
path: '*',
component: index
}
]
})
export default router
3. 路由出口
App.vue
<template>
<div>
<!-- 路由出口 -->
<router-view></router-view>
</div>
</template>
index.vue
<template>
<div>
<h1>这里是首页</h1>
<!-- 二级路由出口 -->
<router-view></router-view>
</div>
</template>
4. 路由跳转
<router-link to="/index" active-class="active">首页</router-link>
<!-- replace:router.replace() -->
<router-link :to="/index" replace></router-link>
this.$router.push('/index')
this.$router.replace('/index')
this.$router.go(-1)
通过 name
属性跳转路由
this.$router.push({
name: 'index',
params: {
id: "001"
}
})
// 重复进入同一个路由报错解决方案
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err);
};
5. 路由传参
**$route:**路由信息
**$router:**路由使用
方式一 : (query)
query传递参数不会出现路参数丢失的情况,所以不需要做其他的配置,不过缺点就是参数会拼接在url后面: url?xx=yy 这种方式来传递参数,会暴露参数,并且url也有字符上限限制
<!-- 传参组件 -->
<router-link v-bind:to="'/index?id='+id">首页</router-link>
this.$router.push({path: '/index', query: {id:1}})
<!-- 收参组件 -->
<script>
export default{
mounted(){
this.$route.query.id
}
}
</script>
方式二: (params)
params传递参数是将参数放在route对象中,没有放在url后面,但是会有一个问题,在跳转之后的页面中刷新的时候,会导致当前路由中保存的params的参数丢失
export default new Router({
routes: [
// 首页
{
path: '/index:id',
component: index,
}
]
})
<!-- 传参组件 -->
<router-link v-bind:to="'/index/'+id">首页</router-link>
this.$router.push({name: 'index', params:{id:1}})
<!-- 收参组件 -->
<script>
export default{
mounted(){
this.$route.params.id
}
}
</script>
方式三 params
// 传参组件
this.$router.push({
name:'detail',
params:{
id: "001"
}
})
// 接参组件
mounted() {
var id = this.$route.params.id;
console.log(id);
}
router.back() 传参
// 传参的页面
this.$route.query.id = '123456';
// 接参的页面
beforeRouteEnter(to, from, next) {
next(vm => {
if (from.name == [上一个页面]") {
// vm指向this实例
vm.id= from.query.id;
}
});
},
6. 登录拦截
// 进入页面时拦截 (全局)
router.beforeEach((to, from, next)=>{
if (to.path==='/login') {
next()
return;
}
next('/login')
})
// 离开页面时拦截 (全局)
router.afterEach((route)=>{
console.log(route);
})
// 指定页面进入的时候 (路由)
export default new Router({
routes: [
{
path: '/index',
component: index,
// beforeEnter
beforeEnter: (to, from, next) => {
next()
}
]
})
<!-- 页面内 (内部) -->
<script>
export default {
props: [],
data() {
return {}
},
// beforeRouteEnter(){ }
beforeRouteEnter (to, from, next) {
next()
},
// beforeRouteLeave(){ }
beforeRouteLeave (to, from, next) {
next()
},
components: {},
mounted() {}
}
</script>
7. 加载方式
// hash 模式
export default new Router({
mode: "history",
routes: [
{
path: '/index',
component: index,
// beforeEnter
beforeEnter: (to, from, next) => {
next()
}
]
})
8. 重定向
{
path: '/datastatist',
name: 'datastatist',
component: () => import('../views/dataStatistics'),
// redirect 重定向
redirect: '/datastatist/index',
children: [
{
path: '/datastatist/index',
name: 'datastatist/index',
component: () => import('../views/dataStatistics/profile-data.vue'),
},
],
},
Vue3 路由重定向
const routes = [
{
path: "/",
name: "index",
component: () => import("/@/views/main/index.vue"),
},
{
path: "/:pathMatch(.*)*",
redirect: { name: "index" }
}
];
23. 懒加载
import index from './index.vue'
const index = ()=>import('./index.vue')
24. axios 数据请求
1. 安装 axios
npm install axios --save
// 请求头设置
axios.defaults.baseURL = "接口地址";
2. 配置代理
config / index.js
proxyTable: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
secure: false, // 如果是https接口,需要配置这个参数
pathRewrite: {
"^/api": "http://localhost:3000"
}
}
}
3. 数据请求
util / request.js
- **GET:**params: { }
- **POST:**data: { }
- **PUT:**data:{ }
import axios from 'axios'
let url = '/api'
// 登录请求
export const loginRequest = (data) => {
return axios({
method: 'post',
url: url+'/login',
headers: { //设置请求头(token)
'token':'xxxxxx'
},
data: data
})
}
pages / login.vue
<script>
// loginRequest --> 引入登录请求
import { loginRequest } from "../util/ajax";
export default {
methods: {
login() {
loginRequest(this.user).then((message)=>{
if (message.data.ok) {
this.$router.push('/index')
} else {
alert(message.data.message)
}
})
}
}
</script>
3.1 axios.all
同时请求多个请求
const axios1 = (data) => {
return axios({
method: 'post',
url: url+'/login',
data: data
})
}
const axios2 = (data) => {
return axios({
method: 'get',
url: url+'/index',
data: data
})
}
// 同时请求两个请求
this.$axios.all([axios1, axios2])
.then(this.$axios.spread((item1, item2) => {
console.log(item1);
console.log(item2);
}
))
4. 请求拦截 响应拦截
import axios from 'axios'
// 请求拦截
axios.interceptors.request.use((req)=>{
return req;
})
// 响应拦截
axios.interceptors.response.use((res)=>{
console.log("请求地址:"+res.config.url);
console.log(res);
return res;
})
let url = '/api'
// 登录请求
export const loginRequest = (data) => {
return axios({
method: 'post',
url: url+'/login',
data: data
})
}
5. 上传文件
手动上传文件 + 获取上传进度
httpRequest(file){
let formdata = new FormData();
formdata.append("file", file.file);
for(let item in this.option){
formdata.append(item, this.option[item]);
}
axios({
url: "http://localhost:6061/upload",
method: "post",
data: formdata,
onUploadProgress: (progress) => {
let num = progress.loaded / progress.total * 100 | 0;
this.uploadlist[this.uploadlist.length - 1].progress = num;
}
}).then((res) => {
console.log(res);
});
},
25. Vuex 状态管理
- 引入vuex
- 定义状态
- main引入,注册
- 组件使用
0. 引入
npm install vuex --save
1. 状态
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
// 状态
const state = {
name: "vue"
}
// 修改状态:函数
const mutations = {
changeName(state, name) {
state.name = name;
}
}
// 动作:修改状态 (有参数时要写上参数)
const actions = {
setName(context, name) {
context.commit('changeName', name);
}
}
// 计算属性
const getters = {
getName(state) {
return state.name
}
}
export default new vuex.Store({
// 状态
state,
// 修改状态:函数
mutations,
// 动作:修改状态
actions,
// 计算属性
getters
})
2. main中注册
import store from './store/index';
new Vue({
el: '#app',
router,
store
})
3. 简单使用
<template>
<div>
<h1>vuex-one</h1>
<p>{{$store.state.name}}</p>
<p class="text" v-if="$store.state.ste">{{$store.state.ste}}</p>
<p class="text active" v-else>{{$store.state.ste}}</p>
<button v-on:click="$store.dispatch('setName', 'javascript')">名字</button>
<button v-on:click="$store.dispatch('setSte')">状态</button>
</div>
</template>
4. 统一导出
<template>
<div>
<h1>vuex-two</h1>
<p>{{getName}}</p>
<p class="text">{{getSte}}</p>
<button v-on:click="setName('javascript')">名字</button>
<button v-on:click="setSte">状态</button>
</div>
</template>
<script>
// 从vuex中导入方法
import { mapGetters, mapActions } from "vuex";
export default {
// 读取仓库里的方法
methods: {
...mapActions(["setName","setSte"])
},
// 读取仓库里的状态
computed: {
...mapGetters(["getName", "getSte"])
},
};
</script>
5. vuex模块化管理
store / index.js
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
// 状态
const state = {
number: 1
};
// 计算属性
const getters = {
number(){ return state.number }
};
// 修改状态
import {mutations} from './mutations'
import {actions} from './actions'
// 状态管理模块
import login from './modules/login'
// 实例化 vuex
export default new vuex.Store({
state,
mutations,
actions,
getters,
modules:{
login
}
})
store / actions.js
/*
动作:状态改变
1. 调用 mutations 文件中封装的函数
2. commit 一下 mutations 中的函数名即可
3. 有参数时加入参数
4. context 固定参数,指向 vuex 本身
5. 组件中使用时使用这里的函数
*/
export const actions = {
add(context) {
context.commit("addNumber");
},
sub(context) {
context.commit("subNumber");
}
};
store / mutatons.js
/*
修改状态
1. 函数:写逻辑函数
2. 所有的逻辑封装到这里的函数里
3. 在 actions 文件中调用这里的函数名
4. 组件中使用时使用的是 actions 中的函数名
5. 有参数时加入参数
6. state 固定参数,指向定义的初始化状态
*/
export const mutations = {
// 计数器增加
addNumber(state) {
console.log(state.number);
if (state.number >= 10) {
state.number = 10
} else {
state.number++
}
},
// 计数器减少
subNumber(state) {
console.log(state.number);
if (state.number <= 1) {
state.number = 1
} else {
state.number--;
}
}
};
store / modules / login.js
const state = {}
const mutations = {}
const actions = {}
const getters = {}
export default {
state,
mutations,
actions,
getters,
namespaced: true
}
26. 本地存储
localStorage
:存储在本地浏览器中,不会删除
// 设置本地存储
localStorage.setItem([key], [val]);
// 获取本地存储
localStorage.getItem([key]);
sessionStorage
:存储在本地浏览器中,窗口关闭会自动删除
// 设置本地存储
sessionStorage.setItem([key], [val]);
// 获取本地存储
sessionStorage.getItem([key]);
监听本地存储
window.onStorag()
27. UI 库
pc端
npm install element-ui --save
移动端
npm install vant --save
28. 扩展补充
1. 设置不同页面的不同title
src / main.js
router.beforeEach((to, from, next)=>{
if(to.meta.title){
document.title = to.meta.title;
}
next();
})
src / router / index.js
export default new Router({
routes: [
{ //首页
path: '/',
component: indexRouter,
//此路由的title
meta: {
title: '我的世界'
}
},
{ //重定向
path: '*',
redirect: '/'
}
]
})
2. 设置title,第二个方法
- 安装
npm install vue-wechat-title --save
- 引入:src / main.js
import vueTitle from 'vue-wechat-title'
Vue.use(vueTitle)
- 设置:src / router / index.js
export default new Router({
routes: [
{ //首页
path: '/',
component: indexRouter,
//此路由的title
meta: {
title: '我的世界'
}
},
{ //重定向
path: '*',
redirect: '/'
}
]
})
- 路由出口使用
src / App.vue
<div>
<router-view v-wechat-title="$route.meta.title"></router-view>
</div>
3. 使用时的不注意
- vuex使用时忘记在 main.js 中注册
- vuex使用时:在
actions
中注册事件有参数的要带参数(在 context 后面写的参数,经常会忘记) - 数据请求时的配置代理
4. 递归组件
**父组件:**注册使用子组件(递归组件),传入数据
<template>
<!-- 列表 -->
<div class="list">
<v-list :list="list"></v-list>
</div>
</template>
<script>
import vList from '../../common/list'
export default {
data() {
return {
list: {},
}
},
components:{
vList
}
}
</script>
子组件:
- 必须要有
name
属性,通过name
属性才能成功递归 name
属性的值就是递归组件调用时的名字- 用
props
接收父组件的传值时,必须规定数据的类型 - 递归组件调用自身时,必须要有
v-if
,确保不会陷入死循环
<template>
<div>
<ul v-for="(value,key) in list" :key="key">
<li><span>{{key}}</span></li>
<!-- 如果数据类型为对象,进行列表循环 -->
<li v-if="typeof(value)==='object'">
<!-- 调用自身 -->
<v-list :list="list[key]"></v-list>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "vList",
props: {
list: Object
}
}
</script>
5. 可视化图表
<template>
<div id="echarts" class="app"></div>
</template>
<script>
// 引入echarts
import echarts from 'echarts'
export default {
data() {
return {
//图表数据
option: {
title: { //图表标题
text: "echarts实例"
},
tooltip: {},
legend: { //
data: ["分数"]
},
xAxis: { //x轴数据
data: ["语文","高数","英语","c语言","C#","C++","asp.net","web"]
},
yAxis: {}, //y轴数据
series: [{
name: '分数',
type: 'bar',
data: [60,42,10,84,41,60,82,90]
}]
}
}
},
mounted() {
var myChart = echarts.init(document.getElementById('echarts'));
myChart.setOption(this.option);
}
}
</script>
6. 插件
1. core-js --> JavaScript 的模块化标准库
import 'core-js'; // <- at the top of your entry point
Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]
[1, [2, 3], [4, [5]]].flat(2); // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32
2. fingerprintjs2 --> 浏览器指纹采集器 获取用户唯一标识码
if (window.requestIdleCallback) {
requestIdleCallback(function () {
Fingerprint2.get(function (components) {
console.log(components) // an array of components: {key: ..., value: ...}
})
})
} else {
setTimeout(function () {
Fingerprint2.get(function (components) {
console.log(components) // an array of components: {key: ..., value: ...}
})
}, 500)
}
3. js-base64 --> base64的编码解码
let latin = 'dankogai';
let utf8 = '小飼弾'
let u8s = new Uint8Array([100,97,110,107,111,103,97,105]);
Base64.encode(latin); // ZGFua29nYWk=
Base64.btoa(latin); // ZGFua29nYWk=
Base64.btoa(utf8); // raises exception
Base64.fromUint8Array(u8s); // ZGFua29nYWk=
Base64.fromUint8Array(u8s, true); // ZGFua29nYW which is URI safe
Base64.encode(utf8); // 5bCP6aO85by+
Base64.encode(utf8, true) // 5bCP6aO85by-
Base64.encodeURI(utf8); // 5bCP6aO85by-
Base64.decode( 'ZGFua29nYWk='); // dankogai
Base64.atob( 'ZGFua29nYWk='); // dankogai
Base64.atob( '5bCP6aO85by+'); // 'å°é£¼å¼¾' which is nonsense
Base64.toUint8Array('ZGFua29nYWk='); // u8s above
Base64.decode( '5bCP6aO85by+'); // 小飼弾
// note .decodeURI() is unnecessary since it accepts both flavors
Base64.decode( '5bCP6aO85by-'); // 小飼弾
4. vuescroll --> 自定义滚动条插件
import Vue from "vue";
import vuescroll from 'vuescroll/dist/vuescroll-native';
import 'vuescroll/dist/vuescroll.css';
Vue.use(vuescroll); // install the vuescroll first
Vue.prototype.$vuescrollConfig = {
mode: 'slide',
bar: {
background: '#e59243',
size: '12px',
onlyShowBarOnScroll: false,
showDelay: 500
},
rail:{
gutterOfSide: '10px'
}
};
5. vue-count-to --> 数据滚动插件
import CountTo from 'vue-count-to';
import Vue from "vue";
import vuescroll from 'vuescroll/dist/vuescroll-native';
import 'vuescroll/dist/vuescroll.css';
Vue.use(vuescroll); // install the vuescroll first
Vue.prototype.$vuescrollConfig = {
mode: 'slide',
bar: {
background: '#e59243',
size: '12px',
onlyShowBarOnScroll: false,
showDelay: 500
},
rail:{
gutterOfSide: '10px'
}
};
https://www.npmjs.com/package/vue-count-to
6. nprogress --> 顶部进度条
$ npm install --save nprogress
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
NProgress.start(); // 开始
NProgress.done(); // 结束
https://github.com/rstacruz/nprogress
8. markdown 编辑器
// v-md编辑器文档
https://code-farmer-i.github.io/vue-markdown-editor/examples/base-editor.html
// vue -> markdown编辑器
https://cn.vuejs.org/v2/examples/index.html
// mavonEditor
https://www.jianshu.com/p/04376d0c9ff1
9. 插件总结
https://www.cnblogs.com/lguow/p/9257289.html
29. is 动态组件 - 标签页
<template>
<div class="app">
<button @click="changView(1)">view1</button>
<button @click="changView(2)">view2</button>
<button @click="changView(3)">view3</button>
<component :is="view"></component>
</div>
</template>
<script>
import view1 from '../common/view1';
import view2 from '../common/view2';
import view3 from '../common/view3';
export default {
data() {
return {
view: 'view1'
}
},
components: {
view1,
view2,
view3
},
methods: {
changView(index){
this.view = "view" + index;
}
},
}
</script>
<style scoped>
</style>
30. Vue.set - 数据修改,视图未更新
Vue.set([原数据,索引,新值])
attribute:元素标签的属性
property:元素对象的属性
<template>
<div class="app">
<ul>
<li v-for="item in data" :key="item.id">{{ item.message }}</li>
</ul>
<button @click="changeMsg">添加</button>
</div>
</template>
<script>
export default {
data() {
return {
data: [
{id: "0", message: "html"},
{id: "1", message: "css"},
{id: "2", message: "javascript"},
],
index: 0
}
},
methods: {
// 添加
changeMsg(){
// 使用 $set 想数据中添加数据
this.$set(this.data, index, {
id: "3",
message: "Vue"
});
}
}
}
</script>
31. parent children
ref:为子组件指定一个索引名称,通过索引来操作子组件
$parent:直接获取父组件的属性和方法
$children:直接获取子组件的属性和方法,可能会有多个子组件,所以获取的是一个数组
parent.vue
<template>
<div class="app">
<button @click="changeMsg">parent-btn</button>
<p>{{ parentMsg }}</p>
<children />
</div>
</template>
<script>
import children from '../common/children';
export default {
name: 'Parent',
props: [],
data() {
return {
parentMsg: 'Vue'
}
},
components: {
children
},
methods: {
// 改变子组件的属性(获取和设置)
changeMsg(){
this.$children[0].childrenMsg = "子组件被改变了";
console.log(this.$children[0].childrenMsg);
}
}
}
</script>
children.vue
<template>
<div class="app">
<button @click="changeMsg">children-btn</button>
<p>{{ childrenMsg }}</p>
</div>
</template>
<script>
export default {
name: 'Children',
props: [],
data() {
return {
childrenMsg: "children"
}
}
methods: {
// 改变父组件的属性(获取和设置)
changeMsg(){
this.$parent.parentMsg = "父组件被改变了";
console.log(this.$parent.parentMsg);
}
}
}
</script>
32. Vue事件总线
- 创建一个Vue实例
- 在实例中通过事件传递数据
// Vue全局事件总线
const EventBus = new Vue();
Vue.prototype.$bus = EventBus; //全局事件总线
this.$bus.$emit('事件名', ['值']);
this.$bus.$on('事件名', (value)=>{
console.log(value);
})
this.$bus.$off('事件名');
33. 图片引入方式
1.
data(){
return {
img: require('@/logo.png');
}
}
2.
import img from '@/img/logo.png';
data(){
return {
img: img
}
}
3. 静态资源引入,直接引入
mounted() {
this.img = 'static/img/logo.png';
}
34. element 问题
1. element-ui 修改样式不显示
- 将 <style> 上的 scoped 删掉
- 在 App.vue 中设置全局样式
2. 下拉框不响应
forceUpdate():手动强制刷新
this.$forceUpdate()
35. 刷新部分组件
/* 样式穿透 */
.el-input /deep/ .el-input__inner{
border: none;
}
/* Vue3 穿透 */
&:deep(.el-input__inner){
border: none;
}
App.vue
和 <keep-alive>
一起使用无效
<template>
<div id="app" @click.stop="dialogyShow">
<router-view v-if="isAlive"/>
</div>
</template>
<script>
// vuex
import { mapActions } from 'vuex';
export default {
name: 'App',
data() {
return{
isAlive: true
}
},
provide() {
return {
reload: this.reload
}
},
methods: {
reload() {
this.isAlive = false;
this.$nextTick(function () {
this.isAlive = true;
});
}
},
}
</script>
index.vue
export default {
inject: ['reload'],
methods: {
handleReload() {
this.reload(); // 在想要刷新页面的时候调用reload方法
}
}
}
36. 使用Echarts
common / echarts.vue
<template>
<div :id="charData.id" ref="e_chart_s" style="display: inline-block;"></div>
</template>
<script>
import echarts from 'echarts';
export default {
name: "Echarts",
props: {
charData: {
type: Object
}
},
data() {
return {
show: true,
};
},
mounted() {
this.$refs.e_chart_s.style.width = this.charData.width + "px";
this.$refs.e_chart_s.style.height = this.charData.height + "px";
this.init();
},
methods: {
init(){
let chars = echarts.init(document.getElementById(this.charData.id));
let option = this.charData.option;
chars.setOption(option);
},
clear(){
let chars = echarts.init(document.getElementById(this.charData.id));
chars.clear();
},
dispose(){
let chars = echarts.init(document.getElementById(this.charData.id));
chars.dispose();
}
}
};
</script>
<style lang="less" scoped>
</style>
common / index.js
import Echarts from './Echarts.vue';
export default {
Echarts
};
main.js
import e_char from './common/index';
import option from './echarts/option';
// 3.全局组件
for(let i in e_char){
Vue.component(i, e_char[i]);
}
// echarts配置
Vue.prototype.$option = option;
view / demo.vue
<template>
<div class="template">
<Echarts :charData="charData" ref="char"></Echarts>
</div>
</template>
<script>
export default {
data(){
return{
charData: {
id: "Echarts",
width: 400,
height: 400,
option: this.$option.Contrast(),
}
};
},
mounted() {
this.init();
},
methods: {
init(){
let name = ["全市", "全校", "班级"];
let data = [10,20,30];
this.$refs.char.dispose();
this.charData.option = this.$option.Contrast(name, data);
this.$refs.char.init();
},
}
};
</script>
option.js
let Contrast = (xName, data) => {
return {
silent: true,
zgrid: {},
xAxis: [],
yAxis: [],
series: []
};
};
export default {
Contrast
};
37. element - upload 上传
<template>
<el-upload
class="upload-demo"
drag
action=""
accept=".xlsx,.xls"
:auto-upload="false"
:on-change="onChange">
<i class="el-icon-upload"></i>
<div class="el-upload__text">上传模板</div>
</el-upload>
<button @click="upload">上传</button>
</template>
<script>
export default {
data() {
return {
formData: {}
}
}
methods: {
// 文件选取
onChange(file){
this.$set(this.formData,'file',file)
},
// 上传文件
upload(){
const formData = new FormData();
formData.append("file", this.formData.file.raw);
formData.append("organizeId", this.$store.getters.userInfo.organizeId);
formData.append("classId", JSON.parse(sessionStorage.getItem("nameSelect")).id);
/*
1. 上传接口
2. 参数 formData
*/
}
}
}
38. vue.config.js
publicPath
-
Type:
string
-
Default:
/
例如,如果你的应用被部署在
https://www.my-app.com/my-app/
,则设置publicPath
为/my-app/
这个值也可以被设置为空字符串 (
''
) 或是相对路径 ('./'
),这样所有的资源都会被链接为相对路径,这样打出来的包可以被部署在任意路径
outputDir
-
Type:
string
-
Default:
'dist'
build
时生产环境的目录,构建时会清除目录使用
--no-clean
可关闭
assetsDir
-
Type:
string
-
Default:
''
静态资源目录
indexPath
-
Type:
string
-
Default:
'index.html'
生成的
index.html
的输出路径 (相对于outputDir
)。也可以是一个绝对路径
pages
-
Type:
Object
-
Default:
undefined
在 multi-page 模式下构建应用。每个“page”应该有一个对应的 JavaScript 入口文件。其值应该是一个对象,对象的 key 是入口的名字,value 是:
- 一个指定了
entry
,template
,filename
,title
和chunks
的对象 (除了entry
之外都是可选的); - 或一个指定其
entry
的字符串。
module.exports = { pages: { index: { // page 的入口 entry: 'src/index/main.js', // 模板来源 template: 'public/index.html', // 在 dist/index.html 的输出 filename: 'index.html', // 当使用 title 选项时, // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title> title: 'Index Page', // 在这个页面中包含的块,默认情况下会包含 // 提取出来的通用 chunk 和 vendor chunk。 chunks: ['chunk-vendors', 'chunk-common', 'index'] }, // 当使用只有入口的字符串格式时, // 模板会被推导为 `public/subpage.html` // 并且如果找不到的话,就回退到 `public/index.html`。 // 输出文件名会被推导为 `subpage.html`。 subpage: 'src/subpage/main.js' } }
- 一个指定了
39. vue3依赖注入
父传子孙数据:provide
子孙得到数据:inject
父 -> 子 传值
// 父组件
import { ref, provide } from 'vue'
setup(){
// 函数, 值
provide('moneyInfo', 1000)
return{
moneyInfo
}
}
//孙子组件
import { inject } from 'vue'
setup(){
const moneyInfo = inject('moneyInfo')
return{
moneyInfo
}
}
子 -> 父 传值
//父组件
import {ref,provide} from 'vue'
setup(){
provide('money',money)
// 封装接收孙子组件数据的函数
const money=(val)=>{
console.log('子孙组件传递的数据',val)
}
}
//子孙组件
<div @click='handleSend'>点击传值</div>
import {inject} from 'vue'
setup(){
const money = inject('money')
const handleSend=() => {
money(200)
}
}
productionSourceMap
-
Type:
boolean
-
Default:
true
如果你不需要生产环境的 source map,可以将其设置为
false
以加速生产环境构建。
引入组件报错
SyntaxError: The requested module ‘/@/components/popover/Popover.vue’ does not provide an export named ‘[组件]’
不提供名为【组件】的导出
import { Popover } from '/@/components/popover/Popover.vue';
引入组件时错误的使用了 {}