vue-cli
需要先安装vue脚手架创建命令:npm i –g @vue/cli
vue create <项目名字>
vue create vue-demo
cd vue-demo
yarn serve
vue-cli管理包设置
初始使用vue命令时,若含多个包管理工具,会进行选择
选择后如需要更改管理包,到C:\Users\username
的.vuerc
修改配置(windows)
.vuerc文件
{
"useTaobaoRegistry": true, // 是否使用淘宝镜像
"packageManager": "yarn", // 使用的包管理工具 npm yarn
}
vue.config.js
vue-cli3 脚手架搭建完成后,项目目录中没有vue.config.js
文件,需要手动创建
官方文档:https://cli.vuejs.org/zh/config/,根据项目需要则进行相应配置
vue指令
<template>
<div>
<!-- v-text 插入文本内容 -->
<p v-text="name"></p>
<p>{{ name }}</p>
<!-- v-html 插入html内容 -->
<p v-text="htmlContent"></p>
<p v-html="htmlContent"></p>
<!-- v-show false不显示,存在标签结构 -->
<p v-text="name" v-show="bool"></p>
<p v-text="name" v-show="false"></p>
<!-- v-if false不显示,标签结构删除 -->
<p v-text="name" v-if="bool"></p>
<p v-text="name" v-if="false"></p>
<!-- v-if v-else v-else-if,必须连用不能中断 -->
<p v-html="name" v-if="type === 'a'"></p>
<p v-html="htmlContent" v-else-if="type === 'b'"></p>
<p v-html="bool" v-else></p>
<!-- v-for 可遍历数组和对象 -->
<!-- 数组:con拿到数组内容,idx拿到数组下标 -->
<div v-for="(con, idx) in arr" v-text="con" :key="idx"></div>
<!-- 对象:con拿到对象的值, idx拿到对象的键 -->
<div v-for="(con, idx) in object" v-text="con" :key="idx"></div>
<!-- v-on 简写@ 事件绑定 -->
<button v-text="name" v-on:click="clickFun"></button>
<button v-text="name" @click="clickFun"></button>
<!-- v-bind 绑定dom属性 -->
<!-- 在绑定 class 或 style 特性时,支持其它类型的值,如数组或对象 -->
<div
:class="{
container: true,
fixed: false
}"
>
katsuki3
</div>
<div :style="{ fontSize: 20 + 'px' }">katsuki</div>
<!-- v-model 表单元素上创建双向数据绑定改变view和model层数据 -->
<input v-model="name" />
</div>
</template>
<script>
export default {
name: 'VueCommand',
data() {
return {
name: 'katsuki',
htmlContent: `<span>18</span>`,
bool: true,
type: 'a',
arr: ['katsuki1', 18],
object: {
name: 'katsuki2',
age: 18
}
}
},
methods: {
clickFun() {
console.log('fun执行')
}
}
}
</script>
自定义指令
自定义指令文档:https://cn.vuejs.org/v2/guide/custom-directive.html
全局指令
main.js
中设置directive
Vue.directive('color', {
bind(el, binding) {
console.log('binding', binding)
el.style.color = binding.value
}
})
任意vue文件
中调用
<p v-text="name" v-color="'pink'"></p>
局部指令
在vue
组件中设置directive
<template>
<div>
<input type="text" v-autoFocus />
</div>
</template>
<script>
export default {
directives: {
// 自定义指令的名字
autoFocus: {
// 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
inserted(el) {
el.focus()
console.log('inserted')
},
// 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
bind() {
console.log('bind')
},
// 所在组件的 VNode 更新时调用。
update() {
console.log('update')
},
// 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
componentUpdated() {
console.log('componentUpdated')
},
// 只调用一次,指令与元素解绑时调用。
unbind() {
console.log('unbind')
}
}
},
}
</script>
vue-router
yarn add vue-router
创建
创建src\routes\index.js
文件,作为路由引入文件
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 引入对应页面
import PageOne from '@/pages/PageOne'
import PageTwo from '@/pages/PageTwo'
// 设置路由
export const routerArr = [
{
path: '/pageOne',
name: 'pageOne',
component: PageOne,
// 路由传参,把一些参数固定的值写在这
// this.$route.meta.bool获取
meta: {
bool: true
}
},
{
path: '/pageTwo',
name: 'pageTwo',
component: PageTwo
}
]
// 配置路由
const routes = [
...routerArr
// 重定向
{ path: '/', redirect: '/pageOne' },
]
const router = new VueRouter({
routes
})
// 路由守卫
// to:将前往的页面路由 from:从哪个页面来的路由 next:下一步操作
router.beforeEach((to, from, next) => {
let token = localStorage.getItem("token");
// 这里用伪token方式验证,在浏览器控制台的localStorage进行设置token值
// 如果token是123的话进去目标页面,否则返回'/pageOne'页面
if (token == 123 || to.path == '/pageTwo') {
next()
} else {
// 如果失败跳转登录页面
router.push({
name: "pageOne"
})
}
// 用定时器测试守卫是否执行
// setTimeout(()=>{
// next()
// },3000)
})
export default router
挂载
main.js
全局挂载
import router from '@/routes'
new Vue({
render: h => h(App),
router,
}).$mount('#app')
vue文件中this.$router
获取实例
标签跳转:<router-link to=”/pageOne”></router-link>
路由内容:<router-view></router-view>
编程式跳转:
// 向history栈添加一个记录,后退返回上个页面
this.$router.push("/PageOne")
// 替换当前history栈中路由,后台返回上上个页面
this.$router.replace("/PageOne")
// n为正数向前,负数向后,跳转n个页面
this.$router.go(n)
配置及使用
src\App.vue
设置路由
<template>
<div id="app">
<p>
<router-link
v-for="(route, idx) in routerArr"
:style="{ marginRight: '15px' }"
:to="route.path"
v-text="route.name"
:key="idx"
/>
</router-link>
</p>
<router-view></router-view>
</div>
</template>
<script>
import { routerArr } from '@/routes'
export default {
name: 'App',
data() {
return {
routerArr
}
}
}
</script>
src\page\pageTwo
<template>
<section>路由测试页</section>
<button @click="toPageOne">点击跳转pageOne</button>
</template>
<script>
export default {
name: 'pageTwo`',
methods: {
toPageOne() {
this.$router.push('/pageOne')
}
},
created() {
// this.$route当前路由信息对象
console.log('this.$route', this.$route)
// this.$router路由实例,用于路由跳转
console.log('this.$router', this.$router)
}
}
</script>
keep-alive
是vue的内置组件,可以使被包含的组件保留状态,避免重新渲染
<!-- 缓存router-view -->
<keep-alive>
<router-view></router-view>
</keep-alive>
props
include
字符串或正则,匹配的组件缓存exclude
字符串或正则,匹配的组件不缓存
// 组件katsuki
exoprt default {
name: 'katsuki',
}
<keep-alive include="katsuki">
<router-view></router-view>
</keep-alive>
router
文件中添加meta参数设置是否需要缓存
// 设置路由
export const routerArr = [
{
path: '/pageOne',
name: 'pageOne',
component: PageOne,
meta: {
keepAlive: true // 需要缓存
}
},
{
path: '/pageTwo',
name: 'pageTwo',
component: PageTwo,
meta: {
keepAlive: false // 不需要缓存
}
}
]
<keep-alive>
<!-- 缓存的视图 -->
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<!-- 不缓存的视图 -->
<router-view v-if="!$route.meta.keepAlice"></router-view>
pageTwo.vue
,使用beforeRouteLeave
变更跳转路由的keepAlive
<script>
export default {
name: 'pageTwo`',
beforeRouteLeave(to, from, next) {
// 设置下个路由的meta
to.meta.keepAlive = false // 让跳转的页面不缓存,刷新
to.meta.keepAlive = true // 让跳转的页面缓存,不刷新
}
}
</script>
嵌套路由
src\App.vue
进行修改
// 设置路由
export const routerArr = [
{
path: '/pageOne',
name: 'pageOne',
component: PageOne,
children: [
{
path: 'childOne',
component: childOne
},
{
path: 'childTwo',
component: childTwo
},
]
}
]
pageOne.vue
中添加如下内容
<template>
<div>
<div>pageOne的内容,router-view显示嵌套路由页内容</div>
<router-link to="/pageOne/childOne">
<span>跳转childOne</span>
</router-link>
<router-link to="/pageOne/childTwo">
<span>跳转childTwo</span>
</router-link>
<router-view></router-view>
</div>
</template>
vue-router传参
存在一个元素,点击能跳转路由
<button @click="toNextRouter(18)">点击跳转</button>
:id方式
刷新不会丢失
methods: {
toNextRouter(age) {
this.$router.push({ path: `/katsuki/${age}` })
}
},
对应路由配置
{
path: '/katsuki/:id',
name: 'katsuki',
component: katsuki
}
跳转的页面获取参数:this.$route.params.age
param方式
刷新会丢失
methods: {
toNextRouter(age) {
// 使用name来匹配路由
this.$router.push({ name: 'katsuki', params: { age } })
}
},
对应路由配置不能包含/:id
跳转页面获取参数:this.$route.params.age
query方式
刷新不会丢失
methods: {
toNextRouter(age) {
this.$router.push({ path: `/katsuki`, query: { age } })
}
},
对应路由配置不能包含/:id
跳转页面获取参数:this.$route.query.id
this.$Watch
<template>
<div>
<div>
<el-input v-model="name" />
<h2 v-text="name"></h2>
</div>
<div>
<el-input v-model="age" />
<h2 v-text="age"></h2>
</div>
<div>
<el-input v-model="user.age" />
<h2 v-text="user.age"></h2>
</div>
</div>
</template>
<script>
export default {
data() {
return {
name: 'katsuki',
age: 18,
user: {
name: 'katsukichan',
age: 18
}
}
},
// 方式一
watch: {
name:(newValue, oldValue) => {
console.log('name的newValue' + newValue)
console.log('name的oldValue' + oldValue)
},
user:{
handler:(newValue, oldValue) => {
// 对象内容变更,获取只能是变更后的值,故oldValue不为旧值
console.log('user.age的newValue值为:' + newValue.age )
console.log('user.age的oldValue值为:' + oldValue.age )
},
deep: true // 深度监视,当对象中的属性发生变化时会被监控
}
},
// 方式二
created() {
this.$watch('age', function (newValue, oldValue) {
console.log('age的newValue值为:'+ newValue +',旧值为:'+ oldValue);
})
},
}
</script>
父子组件通信
传数据和函数(String、Array、Object、Number、Boolean、Function
)
父传子
FatherPage.vue
<template>
<section class="container">
<SonPage :sonData="fatherData" :fatherClick="fatherFun" />
</section>
</template>
<script>
import SonPage from '@/components/SonPage'
export default {
name: 'Hotent',
components: {
SonPageTwo
},
data() {
return {
fatherData: ['katsuki']
}
},
methods: {
fatherFun() {
console.log('父组件方法执行')
}
}
}
</script>
<style lang="less" scoped>
.container {
width: 1200px;
margin: 0 auto;
height: 100vh;
}
</style>
SonPage.vue
<template>
<section>
<div v-text="sonData"></div>
<button @click="fatherClick"></button>
</section>
</template>
<script>
export default {
name: 'SonPage',
props: {
fatherClick: {
type: Function,
default: () => {
console.log('默认执行函数')
}
},
sonData: {
type: Array,
default: () => []
}
}
}
</script>
子传父
SonPage.vue
<template>
<div class="son_container">
<el-button type="primary" @click="emitFun">子传递方法到父</el-button>
</div>
</template>
<script>
export default {
name: 'sonPage',
data() {
return {
sonValue: 18,
}
},
computed: {
sonComputed() {
return 'katsuki' + this.sonValue
}
},
methods: {
emitFun() {
// 传递data的值
this.$emit('sonEmit', this.sonValue)
// 传递computed的值
this.$emit('sonEmit', this.sonComputed)
},
}
}
</script>
FatherPage.vue
<template>
<section class="container">
<SonPage ref="sonPage" @sonEmit="handleSonEmit" />
</section>
</template>
<script>
import SonPage from '@/components/SonPage'
export default {
name: 'FatherPage',
components: {
SonPage
},
methods: {
handleSonEmit(param) {
console.log('获取子组件传递的内容:', param)
// 通过this.$refs获取子组件实例调用函数
this.$refs.sonPage.sonFun()
}
}
}
</script>
<style lang="less" scoped>
.container {
width: 1200px;
margin: 0 auto;
height: 100vh;
}
</style>
this.$bus
bus
是vue实例
中包含的发布订阅者模式
,既$emit $on
,解决非父子组件之间的数据交互问题
main.js
中添加
// 用于无关系组件间的通信
Vue.prototype.$bus = new Vue()
比如有一组同级组件
pageOne
<template>
<div>监听页</div>
</template>
<script>
export default {
name: 'pageOne',
methods: {
busFun(param) {
console.log('param', param)
}
},
created() {
this.$bus.$on('katsuki', this.busFun)
},
destroyed() {
this.$bus.$off('katsuki', this.busFun)
}
}
</script>
pageTwo
<template>
<button @click="busEmit">点击触发emit</button>
</template>
<script>
export default {
name: 'pageTwo',
methods: {
busEmit() {
this.$bus.$emit('katsuki', 18)
}
}
}
</script>
vue插槽
作用:让组件可拓展,更好的复用和做定制化开发
父组件
<template>
<section class="container">
<SlotComponent>
<!-- 默认插槽内容 -->
<div>
<p>这里是默认插槽传入内容</p>
</div>
<!-- 具名插槽内容 -->
<!-- v-slot 只能写在template上 -->
<template v-slot:header>
<p>这里是header内容</p>
</template>
<!-- v-slot缩写# -->
<template #footer>
<p>这里是footer内容</p>
</template>
<!-- 作用域插槽 -->
<!-- 获取子组件的值在这里调用,因为这里传入内容会覆盖里面的默认内容 -->
<!-- 默认插槽方式 -->
<template v-slot="defaultProps">
<div>
<p>{{ defaultProps }}</p>
<p>这里是默认插槽传入内容</p>
</div>
</template>
<!-- 具名插槽方式 -->
<template #scope="slotProps">
{{ slotProps.author }}
</template>
</SlotComponent>
</section>
</template>
<script>
import SlotComponent from '@/components/SlotComponent'
export default {
name: 'SlotUse'
components: {
SlotComponent,
}
}
</script>
子组件
<template>
<div>
<!-- 默认插槽 父组件有传入结构,覆盖当前默认内容 -->
<slot>子组件的默认内容</slot>
<!-- 具名插槽 -->
<div>
<header>
<slot name="header"></slot>
</header>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<!-- 作用域插槽 -->
<!-- 默认插槽方式 -->
<slot :author="author"></slot>
<!-- 具名插槽方式 -->
<div>
<slot name="scope" :author="author"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'SlotComponent',
data() {
return {
author: 'katsuki'
}
}
}
</script>
vuex
yarn add vuex
vuex 是一个专为 vue 应用程序开发的状态管理模式,是一个前端非持久化的数据库中心
刷新页面,会重置vuex的存储的数据
vuex五大概念
State 池 (数据源)
Getter 查 (获取数据源的数据)
Mutation 改 (真正修改的动作)
Action 触发修改行为
Module 可以拥有多个数据源(数据池)
导出仓库
创建src\store\index.js
文件,作为导出文件并引入各个仓库文件
import Vue from 'vue'
import Vuex from 'vuex'
import pageOneStore from './modules/pageOneStore'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
pageOneStore
}
})
挂载仓库
main.js
全局挂载
import store from '@/store'
new Vue({
render: h => h(App),
store,
}).$mount('#app')
vue文件中this.$store
获取实例
仓库使用
src\store\modules\pageOneStore
,数据仓库
// 下文的axios封装
import http from '@/utils/http'
// 定义仓库数据
const state = () => ({
author: 'katsuki',
count: 0,
data: []
})
// 定义数据获取方法,进行数据预处理,同computed
const getters = {
getAuthor(state) {
return state.author + '233'
}
}
// 处理异步操作,把数据传递到mutations
const actions = {
// commit用于触发mutations方法
async getData({ commit }) {
try {
const res = await http.get('/example/data')
commit('setData', res.data)
} catch (err) {
console.error('err', err)
}
}
}
// 修改仓库数据
const mutations = {
increment(state) {
state.count ++
},
setData(state, data = []) {
state.data = data
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
namespaced: true
使其成为带命名空间的模块,vue文件中调用commit
时要加上模块名,如
this.$store.commit('pageOneStore/increment')
this.$store.pageOneStore.author
src\pages\pageOne
,调用页面
<template>
<div>
<p v-text="count"></p>
<p v-text="gettersGetAuthor"></p>
<p v-text="data"></p>
<button @click="incrementCount">点击改count</button>
<button @click="getData">点击获取data</button>
</div>
</template>
<script>
import { mapGetters, mapState } from 'vuex'
export default {
computed: {
// 导入仓库state
...mapState({
count: state => state.pageOneStore.count,
data: state => state.pageOneStore.data
}),
// 导入仓库getters
...mapGetters('katsuki', {
gettersGetAuthor: 'getAuthor'
})
},
methods: {
incrementCount() {
// commit触发mutations
this.$store.commit('pageOneStore/increment')
},
getData() {
// dispatch触发actions
this.$store.dispatch('pageOneStore/getData')
}
}
}
</script>
element-ui
官网:https://element.eleme.cn/#/zh-CN/component/installation
yarn add element
main.js
全局加载,之后就可按照官网文档使用
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
未在main.js文件中直挂载直接调用element组件会报如下错误
Unknown custom element: <el-input>
less/sass
less官网:https://less.bootcss.com/
添加css拓展语言,在打包阶段会转换为css,安装包添加到开发环境即可
yarn add less –D
yarn add less-loader –D
main.js
全局挂载
import less from 'less'
Vue.use(less)
<template>
<div class="container">
<p>katsuki</p>
</div>
</template>
<style lang="less" scoped>
@width: 1200px;
.container {
width: @width;
height: 100vh;
margin: 0 auto;
p {
color: #f00;
}
}
</style>
更多使用方法可查看less官网
axios
axios的npm地址:https://www.npmjs.com/package/axios,里面有介绍如何配置axios
yarn add axios
axios文件
创建src\utils\http.js
文件
在需要引入的文件:import http from '@/utils/http'
也可以导出axiosInstance在main.js
中全局挂载
import axios from 'axios'
// createElement方式
import { showFullScreenLoading, hideFullScreenLoading } from './utils'
// vuex方式
import store from '@/store'
const defaultProxyPrefix = process.env.VUE_APP_DEFAULT_PROXY_PREFIX
const httpRegx = /^http(s?):\/\//
const axiosInstance = axios.create({
timeout: 10000,
responseType: 'json',
header: {
'Content-Type': 'application/json;charset=utf-8'
}
})
// 修改请求url
const withProxy = (url = '') => {
if (httpRegx.test(url)) {
return url
}
return defaultProxyPrefix + url
}
// 请求拦截
axiosInstance.interceptors.request.use((config = {}) => {
let { url, useLoading = true } = config
url = withProxy(url)
// 调用utils方法
useLoading && showFullScreenLoading()
// 调用遮罩仓库actions
useLoading && store.dispatch('request/addRequestCount')
return { ...config, url, useLoading }
})
// 响应拦截
axiosInstance.interceptors.response.use(
(response = {}) => {
const { useLoading } = response.config
// 调用utils方法
useLoading && hideFullScreenLoading()
// 调用遮罩仓库actions
useLoading && store.dispatch('request/subRequestCount')
switch (response.data.code) {
case 200:
return response.data
default:
return response.data || Promise.reject(response.data)
}
},
(err = {}) => {
const { useLoading } = err.config
// 调用utils方法
useLoading && hideFullScreenLoading()
// 调用遮罩仓库actions
useLoading && store.dispatch('request/subRequestCount')
return Promise.reject(err)
}
)
const http = {
get: (url = '', params = {}, config = {}) => axiosInstance.get(url, { params, ...config }),
delete: (url = '', params = {}, config = {}) => axiosInstance.delete(url, { params, ...config }),
post: axiosInstance.post,
put: axiosInstance.put
}
export default http
请求中全局遮罩
vue组件+vuex方式
<template>
<div :class="{ container: loading, container_loading: loading }" v-loading="loading"></div>
</template>
<script>
/*
* @Author: katsuki
* @DESC: 全局遮罩组件
*/
import { mapState } from 'vuex'
export default {
name: 'FullScreenLoading',
data() {
return {
container: 'container',
container_loading: 'container_loading'
}
},
computed: {
...mapState({
loading: state => state.request.requestCount
})
}
}
</script>
<style lang="less" scoped>
.container {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.25s;
transition-delay: 0.2s;
visibility: hidden;
opacity: 0;
}
.container_loading {
&:extend(.container);
visibility: visible;
opacity: 1;
}
</style>
/*
* @Author: katsuki
* @DESC: 全局遮罩仓库
*/
const state = () => ({
requestCount: 0
})
const getters = {
requestRequestCount: state => {
return state.requestCount
}
}
const actions = {
addRequestCount({ commit }) {
commit('add')
},
subRequestCount({ commit }) {
commit('sub')
}
}
const mutations = {
add(state) {
state.requestCount ++
},
sub(state) {
state.requestCount --
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
createElement方式
src\utils\utils.js
,存放公共方法文件,使用createElement
的方式加入简易遮罩
// 构建fullScreenLoading
let isFullScreenLoadingCreate = false
export function _checkFullScreenControl() {
if (!isFullScreenLoadingCreate) {
isFullScreenLoadingCreate = true
let div = document.createElement('div')
div.innerHTML = `<div id="fullScreenLoading" style="position: fixed;background-color: hsla(0,0%,100%,.9);top: 0;right: 0;bottom: 0;left: 0;display: flex;justify-content: center;align-items: center;transition: all 0.25s;transition-delay: 0.2s;visibility: hidden;opacity: 0;">加载中...</div>`
document.body.appendChild(div)
}
}
let isFullScreenLoadingShow = false
// 显示全局加载
export function showFullScreenLoading() {
_checkFullScreenControl()
if (isFullScreenLoadingShow) return
isFullScreenLoadingShow = true
const elem = document.getElementById('fullScreenLoading')
elem.style.visibility = 'visible'
elem.style.opacity = 1
}
// 隐藏全局加载
export function hideFullScreenLoading() {
if (!isFullScreenLoadingShow) return
isFullScreenLoadingShow = false
const elem = document.getElementById('fullScreenLoading')
elem.style.visibility = 'hidden'
elem.style.opacity = 0
}
export default { _checkFullScreenControl, showFullScreenLoading, hideFullScreenLoading }
rimraf
快速删除node_modules
npm i rimraf –g
在要删除文件的目录下cmd:rimraf node_modules