VueCli和Vite
構(gòu)建項(xiàng)目
-
VueCli
Vue CLI 4.x 需要 Node.js v8.9 或更高版本 (推薦 v10 以上)npm install -g @vue/cli # OR yarn global add @vue/cli vue --version 查看vue-cli版本 vue create [projectName] 通過vue-cli創(chuàng)建項(xiàng)目
-
Vite
Vite 需要 Node.js 版本 14.18+站削,16+npm create vite@latest
Vite路由
配置路由
npm install vue-router@next -S 安裝到生產(chǎn)環(huán)境
or
npm install vue-router@4
or
yarn add vue-router@4
src 新建 router/index.js
import { createRouter, createWebHistory } from 'vue-router'
// 還有 createWebHashHistory 和 createMemoryHistory
createRouter({
history: createWebHistory(),
routes: [],
})
映射路由财著、渲染組件
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/home',
name: 'Home',
component: () => import('../views/Home/index.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
新建src/views/Home/index.vue
<template>
<div>這是Home組件</div>
</template>
src/App.vue 設(shè)置渲染區(qū)域
<script setup>
</script>
<template>
<router-view></router-view>
</template>
<style scoped>
</style>
main.js 引入路由
import { createApp } from 'vue'
import App from './App.vue'
// 引入路由
import router from './router'
const app = createApp(App)
app.use(router).mount('#app')
Vite Vuex
安裝vuex
npm install vuex@next --S 安裝到生產(chǎn)環(huán)境
or
yarn add vuex
main.js 引入路由
import { createApp } from 'vue'
import App from './App.vue'
// 引入路由
import router from './router'
+ // 引入store
+ import store from './store'
const app = createApp(App)
- app.use(router).mount('#app')
M app.use(router).use(store).mount('#app')
新建store文件谴蔑,src/store/index.js
import { createStore } from 'vuex'
const store = createStore({
state () {
return { }
},
mutations: {},
getters:{},
actions:{},
modules:{}
})
export default store
Vuex的基礎(chǔ)知識(shí)
數(shù)據(jù)主要存在State中
- 組件直接調(diào)用Actions带兜,Actions再去調(diào)用Mutations,Mutations再去修改State
- 或者組件直接調(diào)用Mutations,Mutations再去修改State
- 要更改 state 必須通過 Mutations 去做更新
定義number,通過 Mutations 去更新,src/store/index.js
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
+ number: 0
}
},
mutations: {
+ UPDATE_NUMBER (state, value) {
+ state.number = value
+ }
},
getters:{},
actions:{},
modules:{}
})
export default store
使用mutations(commit): src/views/Home/index.vue
<template>
<div>這是Home組件</div>
+ <div>這是Store的數(shù)據(jù):{{number}}</div>
</template>
+ <script setup>
+ import { computed } from 'vue'
+ import { useStore } from 'vuex'
+ const store = useStore()
+ setTimeout(() => {
+ store.commit('UPDATE_NUMBER',50)
+ }, 2000)
+ const number = computed(() => store.state.number)
+ </script>
Vuex的Actions
- mutations 是同步的
- actions 是異步的
src/store/index.js
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
number: 0
}
},
+ getters:{},
+ // 同步
mutations: {
+ // store.commit('UPDATE_NUMBER',50)
UPDATE_NUMBER (state, value) {
state.number = value
}
},
- getters:{},
// 異步
actions:{
+ // store.dispatch("NUMBERACTION",200) 調(diào)用
+ // 方法一:
+ // NUMBERACTION(context,payload){
+ // context.commit('UPDATE_NUMBER',payload)
+ // console.log(111,context.state.number) // 想獲取state的數(shù)據(jù)
+ // }
+ // 方法二(拓展方式):
M NUMBERACTION({state,commit},payload){
M commit('UPDATE_NUMBER',payload)
M console.log(111,state.number) // 想獲取state的數(shù)據(jù)
M }
},
modules:{}
})
export default store
使用actions(dispatch): src/views/Home/index.vue
<template>
<div>這是Home組件</div>
<div>這是Store的數(shù)據(jù):{{number}}</div>
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
setTimeout(() => {
M // store.commit('UPDATE_NUMBER',50)
+ store.dispatch('NUMBERACTION',200)
}, 2000)
const number = computed(() => store.state.number)
</script>
Vuex的Actions異步 Promise
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
number: 0
}
},
getters:{},
// 同步
mutations: {
UPDATE_NUMBER (state, value) {
state.number = value
}
},
// 異步
actions:{
NUMBERACTION({state,commit},payload){
+ return new Promise((resolve,reject)=>{
commit('UPDATE_NUMBER',payload)
console.log(111,state.number) // 想獲取state的數(shù)據(jù)
+ resolve() // 成功回調(diào)
+ // reject() // catch回調(diào)
+ })
}
},
modules:{}
})
export default store
使用Promise:src/views/Home/index.vue
<template>
<div>這是Home組件</div>
<div>這是Store的數(shù)據(jù):{{number}}</div>
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
setTimeout(() => {
// store.commit('UPDATE_NUMBER',50)
M store.dispatch('NUMBERACTION',200).then(response => {
+ console.log(response)
+ }).catch((error)=>{
+ console.log(error)
+ })
}, 2000)
const number = computed(() => store.state.number)
</script>
Vuex的Getters 計(jì)算屬性
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
number: 0
}
},
+ // 可以做一些計(jì)算屬性斥难,最終返回的是最新的結(jié)果枝嘶,不會(huì)影響state里面數(shù)據(jù)
getters:{
+ getNumber:(state) => {
+ return state.number + 10
+ }
},
// 同步
mutations: {
UPDATE_NUMBER (state, value) {
state.number = value
}
},
// 異步
actions:{
NUMBERACTION({state,commit},payload){
return new Promise((resolve,reject)=>{
commit('UPDATE_NUMBER',payload)
console.log(111,state.number) // 想獲取state的數(shù)據(jù)
resolve() // 成功回調(diào)
// reject() // catch回調(diào)
})
}
},
modules:{}
})
export default store
使用Vuex的Getters
- src/views/Home/index.vue
<template>
<div>這是Home組件</div>
<div>這是Store的數(shù)據(jù):{{number}}</div>
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
setTimeout(() => {
// store.commit('UPDATE_NUMBER',50)
M // store.dispatch('NUMBERACTION',200).then(response => {
M // console.log(response)
M // }).catch((error)=>{
M // console.log(error)
M // })
+ const result = store.getters['getNumber']
+ console.log('result',result)
}, 2000)
const number = computed(() => store.state.number)
</script>
Vuex的Modules模塊化
解決多業(yè)務(wù)模塊在做state存儲(chǔ)時(shí),字段命名沖突
- 新建store/modules/文件夾哑诊,并新建文件a.js群扶,b.js:
a.js:
+ const state = {
+ name: 10
+ }
+ const getters = {}
+ const mutations = {
+ UPDATE_NUMBER (state, value) {
+ state.number = value
+ }
+ }
+ const actions = {
+ NUMBERACTION (context, payload) {
+ return new Promise((resolve,reject)=>{
+ commit('UPDATE_NUMBER',payload)
+ console.log(111,state.number) // 想獲取state的數(shù)據(jù)
+ resolve(1000) // 成功回調(diào)
+ // reject(1000) // catch回調(diào)
+ })
+ }
+ }
+ export default {
+ namespaced: true, // 命名空間
+ state,
+ getters,
+ mutations,
+ actions
+ }
b.js:
+ const state = {
+ name: 20
+ }
+ const getters = {}
+ const mutations = { }
+ const actions = { }
+ export default {
+ namespaced: true, // 命名空間
+ state,
+ getters,
+ mutations,
+ actions
+ }
- 在store/index.js下導(dǎo)入a.js,b.js:
import { createStore } from 'vuex'
+ import A from "./modules/a"
+ import B from "./modules/b"
const store = createStore({
- state () {
- return {
- number: 0
- }
- },
- // 可以做一些計(jì)算屬性镀裤,最終返回的是最新的結(jié)果竞阐,不會(huì)影響state里面數(shù)據(jù)
- getters:{
- getNumber:(state) => {
- return state.number + 10
- }
- },
- // 同步
- mutations: {
- UPDATE_NUMBER (state, value) {
- state.number = value
- }
- },
- // 異步
- actions:{
- NUMBERACTION({state,commit},payload){
- return new Promise((resolve,reject)=>{
- commit('UPDATE_NUMBER',payload)
- console.log(111,state.number) // 想獲取state的數(shù)據(jù)
- resolve() // 成功回調(diào)
- // reject() // catch回調(diào)
- })
- }
- },
modules:{
+ A,
+ B
}
})
export default store
使用modules:src/views/Home/index.vue
<template>
<div>這是Home組件</div>
<div>這是Store的數(shù)據(jù):{{number}}</div>
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
setTimeout(() => {
+ store.commit('A/UPDATE_NUMBER',50)
// store.commit('UPDATE_NUMBER',50)
M store.dispatch('A/NUMBERACTION',200).then(response => {
M console.log(response)
M }).catch((error)=>{
M console.log(error)
M })
M // const result = store.getters['getNumber']
M // console.log('result',result)
}, 2000)
const number = computed(() => store.state.number)
</script>
Vite 配置 stylus
安裝依賴
npm install -D stylus-loader stylus
新建stylus變量:src/assets/style/stylConfig.styl
+ $red = red
根目錄新建vite.config.js
+ import { defineConfig } from 'vite'
+ import vue from '@vitejs/plugin-vue'
+ import path from 'path'
+ // https://vitejs.dev/config/
+ export default defineConfig(({ command, mode }) => {
+ return {
+ plugins: [vue()],
+ css: {
+ preprocessorOptions: {
+ stylus: {
+ imports: [path.resolve(__dirname, 'src/assets/style/stylConfig.styl')]
+ }
+ }
+ },
+ }
+ })
使用stylus:src/views/Home/index.vue
<template>
M <div class="red-home">這是Home組件</div>
<div>這是Store的數(shù)據(jù):{{number}}</div>
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
setTimeout(() => {
store.commit('A/UPDATE_NUMBER',50)
// store.commit('UPDATE_NUMBER',50)
store.dispatch('A/NUMBERACTION',200).then(response => {
console.log(response)
}).catch((error)=>{
console.log(error)
})
// const result = store.getters['getNumber']
// console.log('result',result)
}, 2000)
const number = computed(() => store.state.number)
</script>
+ <style lang="stylus">
+ .red-home
+ color $red
+ </style>
Vite配置路徑別名
引入配置根目錄vite.config.js:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
return {
plugins: [vue()],
css: {
preprocessorOptions: {
stylus: {
imports: [path.resolve(__dirname, 'src/assets/style/stylConfig.styl')]
}
}
},
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, 'src')
+ }
+ }
}
})
Vite esLint
初始化esLint
// 安裝
npm install eslint -D
// 初始化配置
npx eslint --init
// to check syntax, find problems, and enforce code style
// JavaScript modules (import/export)
// Vue.js
// No Use TypeScript
// Browser Node
// Use a popular style guide
// Standard
// JavaScript
// install them now? Yes
// use npm
// 執(zhí)行完之后,會(huì)在根目錄生成 .eslintrc.cjs 文件
自動(dòng)生成的.eslintrc.cjs
+ module.exports = {
+ env: {
+ browser: true,
+ es2021: true,
+ node: true
+ },
+ extends: [
+ 'standard',
+ 'plugin:vue/vue3-essential'
+ ],
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ ecmaFeatyres: {
+ modules: true
+ },
+ requireConfigFile: false,
+ parser: '@babel/eslint-parser'
+ },
+ plugins: [
+ 'vue'
+ ],
+ rules: {
+ }
+ }
自動(dòng)格式化esLint
// 安裝
npm install vite-plugin-eslint @babel/eslint-parser -D
配置vite.config.js文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
+ import eslintPlugin from 'vite-plugin-eslint'
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
return {
plugins: [
vue(),
+ // 添加下面這塊
+ eslintPlugin({
+ include: ['src/**/*.js', 'src/**/*.vue', 'src/*.js', 'src/*.vue']
+ })
],
css: {
preprocessorOptions: {
stylus: {
imports: [path.resolve(__dirname, 'src/assets/style/stylConfig.styl')]
}
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
}
})
.eslintrc.cjs 添加規(guī)則
// 規(guī)則:http://eslint.cn/docs/rules/
+ module.exports = {
+ env: {
+ browser: true,
+ es2021: true,
+ node: true
+ },
+ extends: [
+ 'standard',
+ 'plugin:vue/vue3-essential'
+ ],
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ ecmaFeatyres: {
+ modules: true
+ },
+ requireConfigFile: false,
+ parser: '@babel/eslint-parser'
+ },
+ plugins: [
+ 'vue'
+ ],
+ rules: {
+ semi: [2, 'never'], // 禁止尾部使用分號(hào)“;”
+ 'no-var': 'error', // 禁止使用 var
+ indent: ['error', 2], // 縮進(jìn)2格
+ 'no-mixed-spaces-and-tabs': 'error', // 不能空格與tab混用
+ quotes: [2, 'single'], // 使用單引號(hào)
+ 'vue/html-closing-bracket-newline': 'off', // 不強(qiáng)制換行暑劝,
+ 'vue/singleline-html-element-content-newline': 'off', // 不強(qiáng)制換行
+ 'vue/multi-word-component-names': 'off',
+ 'vue/max-attributes-per-line': ['error', {
+ singleline: { max: 5 },
+ multiline: { max: 5 }
+ }]
+ }
+ }
vscode配置:文件→首選項(xiàng)→設(shè)置→settings.json
{
+ "[vue]": {
+ "editor.defaultFormatter": "octref.vetur"
+ },
+ "eslint.options":{ // 指定vscode的eslint所處理的文件的后綴
+ "extensions":[
+ ".js",
+ ".vue",
+ ".ts",
+ ".tsx"
+ ]
+ },
+ // 自動(dòng)修復(fù)
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": true
+ }
}
Vite 配置跨域Proxy
本地環(huán)境跨域
server:{
proxy:{
'/api':{
target: 'xxxxx', // 代理的目標(biāo)地址
changeOrigin: true,
secure: true, // 是否https接口
ws: true, // 是否代理websockets
rewrite: (path) => path.replace(/^\/api/,'')
}
}
}
初始化axios
// 安裝
npm install axios -S
刪減:src/views/Home/index.vue
<template>
<div class="red-home">這是Home組件</div>
<div>這是Store的數(shù)據(jù):{{number}}</div>
</template>
<script setup>
- import { computed } from 'vue'
- import { useStore } from 'vuex'
- const store = useStore()
- setTimeout(() => {
- store.commit('A/UPDATE_NUMBER',50)
- store.dispatch('A/NUMBERACTION',200).then(response => {
- console.log(response)
- }).catch((error)=>{
- console.log(error)
- })
- }, 2000)
- const number = computed(() => store.state.number)
</script>
<style lang="stylus">
.red-home
color $red
</style>
關(guān)于舊的全局掛在axios方式:
app.config.globalProperties
一個(gè)用于注冊(cè)能夠被應(yīng)用內(nèi)所有組件實(shí)例訪問到的全局屬性的對(duì)象骆莹。
這是對(duì) Vue 2 中 Vue.prototype 使用方式的一種替代,此寫法在 Vue 3 已經(jīng)不存在了担猛。
與任何全局的東西一樣幕垦,應(yīng)該謹(jǐn)慎使用。
由此可知傅联,vue3其實(shí)并不支持這種寫法先改,但是也可以用,這里只做知識(shí)拓展蒸走,不建議使用
main.js:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
+ import axios from 'axios'
const app = createApp(App)
// 全局載入
+ app.config.globalProperties.$_api = axios
app.use(router).use(store).mount('#app')
--------------------------------
src/views/Home/index.vue:
<script setup>
+ import { getCurrentInstance } from 'vue'
+ const { proxy } = getCurrentInstance()
+ proxy.$axios.post('/xxx)
</script>
了解同源策略
/**
* 同源策略
* 1.http\https
* 2.端口
* 3.www.baidu.com\mail.163.com
*/
跨域重定向:配置vite.config.js文件
// https://cn.vitejs.dev/config/server-options.html#server-proxy
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import eslintPlugin from 'vite-plugin-eslint'
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
return {
plugins: [
vue(),
// 添加下面這塊
eslintPlugin({
include: ['src/**/*.js', 'src/**/*.vue', 'src/*.js', 'src/*.vue']
})
],
css: {
preprocessorOptions: {
stylus: {
imports: [path.resolve(__dirname, 'src/assets/style/stylConfig.styl')]
}
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
+ server: {
+ proxy: {
+ /**
+ * 1仇奶、匹配接口地址前面的字符是 /api,如果成功便會(huì)進(jìn)入重定向
+ * 2比驻、接口組合该溯,用target屬性的地址拼接請(qǐng)求地址
+ */
+ '/api': {
+ // 前兩個(gè)必填
+ target: 'http://xxx.cn',
+ changeOrigin: true,
+ // secure: true, // 是否https接口
+ // ws: true // 是否代理websockets
+ // rewrite: (path) => path.replace(/^\/api/, '') // 匹配開頭為/api 替換為空
+ }
+ }
+ },
}
})
Vite 配置環(huán)境變量
了解環(huán)境變量
開發(fā)項(xiàng)目時(shí)環(huán)境主要分為:
1、本地環(huán)境 == 在電腦開發(fā)項(xiàng)目
2别惦、開發(fā)環(huán)境 == 部署在服務(wù)器環(huán)境(可以是朗伶,公司內(nèi)部搭建的服務(wù))// 192.168.110.184:8000
3、測(cè)試環(huán)境 == 部署在服務(wù)器環(huán)境(可以是步咪,公司內(nèi)部搭建的服務(wù))// 192.168.110.184:8001
4论皆、生產(chǎn)環(huán)境 == 部署在服務(wù)器環(huán)境,已經(jīng)是對(duì)外公布的環(huán)境,可通過正式域名訪問的環(huán)境
5点晴、灰色環(huán)境 == 比較小型數(shù)據(jù)應(yīng)用(公司內(nèi)部測(cè)試感凤,也是屬于生產(chǎn)環(huán)境的一種)
參考文檔:Vite 官方中文文檔-環(huán)境變量和模式
Vite 在一個(gè)特殊的 import.meta.env
對(duì)象上暴露環(huán)境變量:
// 通過內(nèi)建變量來判斷運(yùn)行環(huán)境
console.log('import.meta.env.MODE', import.meta.env.MODE)
console.log('import.meta.env.PROD', import.meta.env.PROD)
console.log('import.meta.env.DEV', import.meta.env.DEV)
console.log('import.meta.env.SSR', import.meta.env.SSR)
環(huán)境變量定義
.env文件:
運(yùn)行 npm run dev ,會(huì)自動(dòng)讀取 .env.development 文件
運(yùn)行 npm run build 粒督,會(huì)自動(dòng)讀取 .env.production 文件
tips:為了防止意外地將一些環(huán)境變量泄漏到客戶端陪竿,只有以VITE_
為前綴的變量才會(huì)暴露給經(jīng)過 vite 處理的代碼:
VITE_SOME_KEY=123
DB_PASSWORD=foobar
只有VITE_SOME_KEY
會(huì)被暴露為import.meta.env.VITE_SOME_KEY
提供給客戶端源碼,而DB_PASSWORD
則不會(huì)屠橄。
根目錄新建.env.development文件
+ VITE_API = http://xx.dev.cn
根目錄新建.env.production文件
+ VITE_API = http://xx.pro.cn
配置不同環(huán)境的變量
在某些情況下族跛,若想在vite build
時(shí)運(yùn)行不同的模式來渲染不同的標(biāo)題,你可以通過傳遞--mode
選項(xiàng)標(biāo)志來覆蓋命令使用的默認(rèn)模式锐墙。例如礁哄,如果你想在 staging (預(yù)發(fā)布)模式下構(gòu)建應(yīng)用:
vite build --mode staging
還需要新建一個(gè) .env.staging 文件:
# .env.staging
VITE_APP_TITLE=My App (staging)
模式
在某些情況下,若想在vite build
時(shí)運(yùn)行不同的模式來渲染不同的標(biāo)題溪北,你可以通過傳遞--mode
選項(xiàng)標(biāo)志來覆蓋命令使用的默認(rèn)模式桐绒。
1、本地環(huán)境 npm run dev
2之拨、開發(fā)環(huán)境 package.json: "build:dev" npm run build --mode dev
3茉继、測(cè)試環(huán)境 package.json: "build:test" npm run build --mode test
4、生產(chǎn)環(huán)境 npm run build 自動(dòng)讀取.env.production
5蚀乔、灰度環(huán)境
--mode dev 需要新建一個(gè) .env.dev 文件:
+ VITE_API = 192.168.110.184:8000
--mode test需要新建一個(gè) .env.test 文件:
+ VITE_API = 192.168.110.184:8001
在vite.config.js中應(yīng)用:
// https://cn.vitejs.dev/config/server-options.html#server-proxy
M import { defineConfig,loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import eslintPlugin from 'vite-plugin-eslint'
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
+ // 根據(jù)當(dāng)前工作目錄中的 `mode` 加載 .env 文件
+ // 設(shè)置第三個(gè)參數(shù)為 '' 來加載所有環(huán)境變量烁竭,而不管是否有 `VITE_` 前綴。
+ // js ts 文件需要引入loadEnv
+ // .vue 文件直接用 import.meta.env.DEV
+ const env = loadEnv(mode, process.cwd(), '')
+ console.log('env', env)
return {
plugins: [
vue(),
// 添加下面這塊
eslintPlugin({
include: ['src/**/*.js', 'src/**/*.vue', 'src/*.js', 'src/*.vue']
})
],
css: {
preprocessorOptions: {
stylus: {
imports: [path.resolve(__dirname, 'src/assets/style/stylConfig.styl')]
}
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
proxy: {
/**
* 1吉挣、匹配接口地址前面的字符是 /api颖变,如果成功便會(huì)進(jìn)入重定向
* 2、接口組合听想,用target屬性的地址拼接請(qǐng)求地址
*/
'/api': {
// 前兩個(gè)必填
M target: env.VITE_API, // 192.168.110.184:8000/8001
changeOrigin: true,
// secure: true, // 是否https接口
// ws: true // 是否代理websockets
// rewrite: (path) => path.replace(/^\/api/, '') // 匹配開頭為/api 替換為空
}
}
},
}
})