Vue3.0官方网站;
国内NPM源;
最新稳定版
npm install vue@next
yarn global add @vue/cli
或
npm install -g @vue/cli
vue upgrade --next
名字要全部小写:大写报错
Warning: name can no longer contain capital letters
vue create <项目文件夹名称>
这里直接选择的第二项:Vue 3;
第三项是 手动选择设置;
见上图代表创建 空白项目成功;
NPM安装新版路由
npm install --save vue-router@next
package.json文件内:下图代表安装完成
-------
Vue实例配置引入路由
创建如图文件:配置涉及的相关文件如下
router/index.js
main.js
App.vue
HelloWorld.vue
python.vue
router/index.js文件[ 生成路由实例 ]
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{ path: '/', redirect: '/user' },
{
path: '/user', component: () => import('@/components/HelloWorld.vue'), children: [
// 子路由路径开头不带斜杠/
{ path: 'money', component: () => import('@/components/python.vue') }
]
}
]
// createRouter替换旧版的new Router()
const router = createRouter({
history: createWebHistory(),
routes
})
export default router;
项目入口文件main.js中引入router实例
main.js中引入 3.2.1中router/index.js中创建的router实例
main.js相关内容:
import { createApp } from 'vue'
import App from './App.vue'
// 引入router
import router from './router'
var app = createApp(App);
// 应用router
app.use(router);
app.mount('#app')
配置路由地址对应组件页面显示位置 [ router-view标签 ]
项目 App.vue 根组件:
配置router-view标签,承载路由对应的组件内容;
App.vue 文件内容 :[ 根组件 ]
<template>
<div>我是蓝色,在我之下,展示一级路由地址的对应组件内容div>
<router-view>router-view>
template>
<script>
export default {
name: 'App'
}
script>
<style>
#app{
background: skyblue;
padding: 30px;
}
style>
HelloWorld.vue内容:[ 一级路由对应组件 ]
<template>
<div class="hello">
<div>--router-view--div>
我是绿色,我是一级路由地址的组件内容; 在我之下是二级路由地址装载的组件位置;
<router-view>router-view>
div>
template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
},
};
script>
<style scoped>
.hello {
background: green;
padding: 20px;
}
style>
python.vue内容: [ 二级路由对应组件 ]
<template>
<div class="hello">
<div>--router-view--div>
我是红色
我是装载进来的二级路由组件
div>
template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
script>
<style scoped>
.hello{
background: red;
padding: 20px;
}
style>
Vuex
组成及经典关系图
组成:
一、State
二、Getters
三、Mutations
四、Actions
五、Modules
六、vuex-persistedstate [ 刷新页面,项目重新加载,vuex 会重置,所有状态回到初始状态,使用 vuex-persistedstate
可以避免这种情况 ]
经典关系图:
1. Components[ 页面 ]
--> 2. Dispatch 触发Actions中的方法
--> 3. 被触发的Action方法内部Commit触发Mutations去直接更改State状态
--> 4. State状态变更影响Render渲染
--> 5. Components [ 页面 ] 刷新
State
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
count: computed(() => store.state.count)
}
}
}
Getters
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
double: computed(() => store.getters.double)
}
}
}
Mutations
注意点简介
Mutation 必须是同步函数
1. 直接更改 Vuex 的 Store 中的状态的【唯一方法】是 mutation中;
2. 默认接受 state 作为第一个参数
3. 不能直接调用一个 mutation 处理函数;
4. 必须 store.commit('increment') 调用
5. store.commit('increment', {amount: 10}) :可传入额外的参数 ;额外参数应该是一个对象,这样可以包含多个字段
, 并且记录的 mutation 会更易读 ;
2 个组成部分
每个 mutation 都 2 个组成部分:
1.一个字符串的事件类型 (type)
2.一个回调函数 (handler)
mutations: {
increment (state, payload) { // 'increment '就是一个字符串的事件类型 (type);
state.count += payload.amount // 更改state的函数体:一个回调函数 (handler)
}
}
常量替代 Mutation 事件类型
创建单独文件放置这些Mutation常量:
作用:归纳所有更改state的Mutation,多人协作的大型项目中,这会很有帮助;
// mutation-types.js 单独文件
export const SOME_MUTATION = 'SOME_MUTATION'
store.js文件对应变更:
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = createStore({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) { // SOME_MUTATION位置是一个字符串的事件类型 (type)
// 修改 state
}
}
})
组件中提交 Mutation
1. this.$store.commit('xxx') ;
2. 使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
注意点简介
Action 提交的是 mutation,【 不能 】直接变更状态;
Action 可以包含任意【 异步 】操作;
参数: 一个与 store 实例具有相同方法和属性的 context 对象;
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
ES2015 的参数解构来简化代码;
actions: {
increment ({ commit }) {
commit('increment')
}
}
分发 Action[ 触发Action ]:
store.dispatch('increment')方法触发;
额外参数:[ 第二参数 ] 类似 Mutation
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
组件中分发 Action[ 触发Action ]:
this.$store.dispatch('xxx');
mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
组合 Action:
1. store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise
2. store.dispatch 仍旧返回 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
store.dispatch('actionA').then(() => {
// ...
})
另外一个 action 中也可以:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
利用 async / await,我们可以如下组合 action:
// 假设 getData() 和 getOtherData() 返回的是 Promise
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
Module
待补充
创建一个基础Store;
目录结构
store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
index.js ,创建store实例
index.js文件:创建store实例
import { createStore, createLogger } from 'vuex'
import cart from './modules/cart'
import products from './modules/products'
const debug = process.env.NODE_ENV !== 'production'
export default createStore({
modules: {
cart,
products
},
strict: debug,
plugins: debug ? [createLogger()] : []
})
使用modules模块化管理store
modules/cart.js
// 接口
import shop from '../../api/shop'
// state
const state = () => ({
name: '矿泉水',
number: 2,
successful: ''
})
// getters
const getters = {
cartProducts: (state, getters, rootState) => {
return {
name: state.name
}
},
cartTotalPrice: (state, getters) => {
return getters['cartProducts'].name + state.number;
}
}
// actions
const actions = {
async checkout({ commit, state }, products) {
try {
// 请求数据
await shop.buyProducts(products)
// 获取数据后commit触发Mutation更改或保存一些需要的state
commit('setCheckoutStatus', 'successful')
} catch (e) {
// 请求失败\报错处理;
console.error(e)
commit('setCheckoutStatus', 'failed')
}
}
}
// mutations
const mutations = {
setCheckoutStatus(state, isOK) {
state.successful = isOK;
}
}
export default {
state,
getters,
actions,
mutations
}
项目入口文件引入store实例
入口文件main.js 引入store实例
import { createApp } from 'vue'
import App from './components/App.vue'
import store from './store'
const app = createApp(App)
app.use(store)
app.mount('#app')
axios 封装
:搭建Http请求API接口文件;安装
: npm install axios --save
创建axios实例
: tool/request.js
import axios from 'axios'
// 创建axios
const service = axios.create({
baseURL: '你的请求前缀/api',
timeout:5000
});
// 添加请求拦截器
service.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
// 做一些统一的处理
// 对于一些特别的请求拦截
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
service.interceptors.response.use(function (response) {
// 对响应数据做点什么
console.log('res',response);
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default service;
业务api.js中载入axios封装实例
; 例如: userApi.js
import service from "../tool/request"
// get获取,无参数
export function getSeller(){
// 返回一个Promise对象
return service.request({
method:'get',
url:'/seller'
})
}
// get获取,有参数
export function getParams(params){
// 返回一个Promise对象
return service.request({
method:'get',
url:'/seller',
params
})
}
// get删除,拼接
export function del(id){
// 返回一个Promise对象
return service.request({
method:'get',
url:'/seller' + id
})
}
// post的默认传参方式data
export function updateData(data){
// 返回一个Promise对象
return service.request({
method:'post',
url:'/seller',
data
})
}
// post的params传参方式
export function updateParams(params){
// 返回一个Promise对象
return service.request({
method:'post',
url:'/seller',
params
})
}
组件中调用接口
; 组件中调用userApi.js接口文件中的接口;
...
import {getSeller} from '../api/userApi.js'
...
async getData(){
// getSeller是一个Promise对象
const {data:res} = await getSeller()
this.allData = res
// 数据从小到大排序
this.allData.sort((a,b)=>{
return a.value - b.value
})
},
Vue-Cli 环境变量文件: [ .env ]
项目根目录中放置 .env
: 项目根目录中放置下列文件来指定环境变量:
.env # 在所有的环境中被载入
.env.local # 在所有的环境中被载入,但会被 git 忽略
.env.[mode] # 只在指定的模式中被载入
.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略; 只在本地有效的变量 .local
.env 文件内部变量定义规则
: 仅识别 三 种规格变量;
1. NODE_ENV
2. BASE_URL
3. 以 VUE_APP_ 开头的自定义变量 ;示例:第七大节7.1中配置反向代理中的 VUE_APP_LOGIN_API ;
.env.development 简例:
# 开发环境配置
ENV = 'development'
# Golf系统
VUE_APP_BASE_API = '/dev-api'
# request.js 封装的axios的Http请求实例 启用了baseURL,
# 那么所有的api.js中定义的业务接口url,实际发起请求时都在此url基础上加了一层前缀 '/dev-api'
# 所以下方 VUE_APP_LOGIN_API /dev-api/login-api 写法只是为了拼凑在反向代理中匹配到此特殊路径标识时,执行反向代理;
# 上述场景应用在 一个项目中需要代理多个不同跨域;
VUE_APP_LOGIN_API = '/dev-api/login-api'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true
变量访问:
console.log(process.env.VUE_APP_LOGIN_API )
vue.config.js
本地跨域反向代理
:项目中相关数据搜集、梳理
; 业务接口api.js文件:
此处假设tool/request.js文件中axios封装实例已经开启baseURL属性赋值:
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
// timeout: 5000,
});
开启baseURL代表意义:
实际http请求在业务api接口的url基础上加上一层前缀;
业务地址:url:'/login-api/seller' ;
实际请求地址:process.env.VUE_APP_BASE_API + '/login-api/seller' ;
import service from "../tool/request"
// get获取,无参数
export function getSeller(){
// 返回一个Promise对象
return service.request({
method:'get',
url:'/login-api/seller'
})
}
开始配置反向代理
: vue.config.js 文件内: proxy属性;
proxy: {
[process.env.VUE_APP_LOGIN_API ]: {
target: `http://www.baidu.com/api/shop`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_LOGIN_API ]: ''
}
}
}
上述 代码分析:
第六节:6.2小节 .env.development 简例:获取环境变量 process.env.VUE_APP_LOGIN_API 结果为 '/dev-api/login-api';
1. 发起Http请求,检测请求路径中是否匹配 [ process.env.VUE_APP_LOGIN_API ],即 '/dev-api/login-api';
2. 如果匹配到,那么将路径'/dev-api/login-api' 左侧替换为 target属性的值:`http://www.baidu.com/api/shop` ,
即要跨的域;
3. pathRewrite :替换路径中的 '/dev-api/login-api' 为空;