Vue 3 是 Vue 当前的最新主版本。它包含了一些 Vue 2 中没有的新特性 (比如 Teleport、Suspense,以及多根元素模板)。同时它也包含了一些与 Vue 2 非兼容性的变更
使用须知
文中举例,默认 yarn 是已经安装了,如果没有安装,请先运行以下命令安装后;
npm install yarn -g
创建项目
模式一
yarn create vite || npm init vue@latest //之后根据提示即可
cd xxx
yarn add vue-router@4 || npm install vue-router@4 //安装路由插件
yarn add pinia || npm install pinia //安装状态管理 可选
yarn add @types/node -D || npm i @types/node -D //配置路径别名 可选
yarn && yarn dev
模式二
yarn create vite myapp –template vue //vue项目的默认配置
执行 npm init vue@latest
后,在创建项目的时候会提示配置项:既第三方库
√ Project name: 项目名称
√ Add TypeScript? javascript的超集
√ Add JSX Support? JSX 支持
√ Add Vue Router for Single Page Application development? Vue路由管理
√ Add Pinia for state management? 类似Vuex的状态管理机制
√ Add Vitest for Unit Testing? vite提供的单元测试框架
√ Add Cypress for End-to-End testing? 自动化测试框架
√ Add ESLint for code quality? 解析规范化代码
√ Add Prettier for code formatting? ... No / Yes
配置
vite 在创建项目xxx后,可以命令进入,vite默认启动的IP是错误的,可以通过配置进行修改;
import path from 'path'
export default defineConfig({
// ...
server:{
open: true,
port: 5173,
host: "0.0.0.0"
},
resolve: {
alias: {
// '~/': `${path.resolve(__dirname, 'src')}/`,
// '@': `${path.resolve(__dirname, 'src')}/`,
'@': join(__dirname, "src"),
}
}
});
"><!-- 配置路径别名
```json tsconfig.json
// ...
"allowJs": true, //允许js
"baseUrl": "./", //基础路径,默认为当前根目录
"paths":{
"@/*": ["src/*"] //@ 别名
}
``` -->
## 示例代码
```js 入口文件 main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import Router from "./Router.js"
import { Store } from '@/stores'
const Vue = createApp(App)
Vue.use(Router)
Vue.use(Store)
Vue.mount('#app')
import {createRouter,createWebHashHistory} from 'vue-router'
// 组件
import Home from './Home.vue'
import Word from './Word.vue'
import About from './About.vue'
import Nofined from './Nofined.vue'
const routes:any = [
{ path: '/', component: Home },
{ path: '/about', component: About,
children:[
{path:'word',component:Word},
]
},
{ path:'/404', component: Nofined},
{ path:'/:catchAll(.*)',redirect:'/404'}
]
const router:any = createRouter({
history: createWebHashHistory(),
routes,
})
export default router
import { createPinia,defineStore } from "pinia";
export const Store = createPinia()
export const useStore = defineStore('main',{
state:()=>({
counter:0
}),
getters:{
double:(state)=> state.counter + 1,
},
actions:{
addNum(){
this.counter++
}
}
})
<script setup>
import { useStore } from '@/stores/index.js'
const store = useStore()
console.log(store.counter)
const clickAb =()=>{
store.counter++
// store.$patch({
// counter: store.counter + 1,
// })
}
</script>
<template>
<div @click="clickAb">About{{store.counter}}
<Home/>
</div>
</template>
测试配置
Vitest 是一个由 Vite 提供支持的极速单元测试框架,@vue/test-utils 则是用来测试vue组件的可选工具
yarn add -D vitest || npm install -D vitest
yarn add -D @vue/test-utils || npm install -D @vue/test-utils
配置命令,给 TypeScript 配置。如下
// ...
export default defineConfig({
plugins: [vue()],
test: {
_// ..._
},
})
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage",
"vitestui": "启动vitest的UI界面",
"vitestui": "vitest --ui"
}
}
测试示例
<template>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
</div>
</template>
import { mount } from "@vue/test-utils";
import App from '../src/App.vue'
import { describe,expect, test } from "vitest";
// 测试分组
describe("App.vue", () => {
// 拥有img元素,拥有logo class
test("has logo img",()=>{
const wrapper = mount(App);
expect(wrapper.find('[class="logo"]').exists()).toBe(true)
expect(wrapper.find('img').exists()).toBe(true)
})
});
相关扩展
在项目中配置模块的相关目录
__tests__/ (Vitest测试文件)
|——App.test.ts
......
src/
|——api/ (所有请求:用户相关,功能相关等)
|——user.js
|——assets/ (静态资源:图片,视频,字体等)
|——images/
|——components/ (所有组件:主页,登录,注册等)
|——Home.vue
|——config/ (所有配置项:网关,路由字体大小等)
|——geteway.js
|——router.js
|——stores/ (pinia的数据中心)
|——index.js
|——home.js
|——utils/ (工具集合:请求,常用函数等)
|——require.js
min.js
在手机端设置区分前进后退的代码
//区分前进后退,主要是为了在移动端添加不同的动画
Vue.prototype.push = function(url){ //路由前进
let old=this.$router.currentRoute.path
if(old==url){
this.$router.go(0);
}else{
Vue.prototype.transitionName = "slide-left"; //通过设置不同的class触发动画
this.$router.push(url);
}
};
Vue.prototype.pop = function(n=-1){ //路由后退
Vue.prototype.transitionName = "slide-right";
let path = this.$router.currentRoute.path;
if (path == "/") { return;}
this.$router.go(n);
}
在本地项目中配置proxy解决跨域请求
这是 vite
工具的配置,在项目的根目录创建 vite.config.js
module.exports = {
server: { //请求配置
proxy: {
'/api': {
target: 'http://10.31.27.56:8087/',
changeOrigin: true, // 是否跨域
rewrite: path => path.replace('/^\/api/', '/api'),
},
},
}
}
在项目中配置接口请求和数据异步处理
yarn add axios
yarn add qs
import axios from "axios"
import qs from "qs"
// 设置超时
axios.defaults.timeout = 5000;
// 表示跨域请求时是否需要使用凭证
axios.defaults.withCredentials = false;
// 允许跨域
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
axios.defaults.headers.post["Access-Control-Allow-Origin-Type"] = "*";
// 请求拦截器
axios.interceptors.request.use(function (config) {
//数据格式化
if (config.method === "post" ||config.method === "put" ||config.method === "delete") {
config.data = qs.parse(config.data);
}
//添加身份验证
// if(config.headers.Authorization === undefined) {
// config.headers.Authorization = 'Bearer ' + store.state.token
// }
return config;
}, error => {
console.log(error.data.error.message) // 也可以通过组件的message抛出
return Promise.reject(error.data.error.message);
})
// 响应拦截器
axios.interceptors.response.use(function (config) {
if (config.status === 200 || config.status === 204) {
return Promise.resolve(config);
} else {
return Promise.reject(config);
}
},function (error) {
if (error.response.status) {
switch (error.response.status) {
case 400:
console.log("发出的请求有错误,服务器没有进行新建或修改数据的操作==>" + error.response.status)
break;
case 401: //未登录 重定向,跳转登录页面
console.log("token:登录失效==>" + error.response.status + ":" + store.state.Roles)
// router.replace({ path: '/Login', });
break;
case 403: //token过期,清除本地token和清空vuex中token对象,跳转登录页面
console.log("用户得到授权,但是访问是被禁止的==>" + error.response.status)
break;
case 404: //资源不存在
console.log("网络请求不存在==>" + error.response.status)
break;
case 406:
console.log("请求的格式不可得==>" + error.response.status)
break;
case 410:
console.log("请求的资源被永久删除,且不会再得到的==>" + error.response.status)
break;
case 422:
console.log("当创建一个对象时,发生一个验证错误==>" + error.response.status)
break;
case 500:
console.log("服务器发生错误,请检查服务器==>" + error.response.status)
break;
case 502:
console.log("网关错误==>" + error.response.status)
break;
case 503:
console.log("服务不可用,服务器暂时过载或维护==>" + error.response.status)
break;
case 504:
console.log("网关超时==>" + error.response.status)
break;
default:
console.log("其他错误错误==>" + error.response.status)
}
return Promise.reject(error.response);
} else {
// 处理断网的情况
// store.commit('changeNetwork', false);
}
})
// 封装的require请求
export const require = (option)=>{
return new Promise((fulfill,reject)=>{
Vue.axios(option).then((res)=>{
fulfill(res)
}).catch((rej)=>{
if(rej.response){
reject(rej.response)
}else if(rej.require){
reject(rej.require)
}else{
reject(rej.message)
}
})
})
}
export default require;
// export const network='https://api.ixiaowai.cn'
export const network='' //通过vue-cli设置服务器代理,在上线前需要改正
export const auto='api'
module.exports={
devServer:{
proxy:{
"/api":{
target: 'https://api.ixiaowai.cn/',
changeOrigin:true,
pathRewrite:{ //重写请求路径,将api标记清除
'^/api':''
}
},
}
}
}
import requires from '@/utils/require'
import {network,auto} from '@/config/geteway'
export const saying=function(params){ //接口请求
return require({
url:`${network}/${auto}/ylapi.php`,
method:'GET',
params
})
}
问题
响应式的边界问题
需要注意的是,相比较vue2,vue3中的原始数据不是响应式的
export default {
data() {
return {
someObject: {}
}
},
mounted() {
const newObject = {}
this.someObject = newObject
console.log(newObject === this.someObject) // false
}
}
Vue的代码风格:选项式API和组合式API
与选项式API不同的是,组合式API需要搭配 <script setup>
进行使用;
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
涉及知识
Vite
官网:https://cn.vitejs.dev/ Vite是一个前端构建工具,它包含一个开发服务器,一套构建指令。vite 以当前工作目录作为根目录启动开发服务器。你也可以通过 vite serve some/sub/dir 来指定一个替代的根目录。 注意的是 index.html 与项目根目录,vite以index.html作为入口文件。也支持多个 .html 作入口点的 多页面应用模式
Vitest
Vitest 是一个由 Vite 提供支持的极速单元测试框架
TypeScript
vite天然支持TypeScript,Vite 仅执行 .ts 文件的转译工作,并 不 执行任何类型检查。并假设类型检查已经被你的 IDE 或构建过程接管了。
Vue Router
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。
Pinia
Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态,Pinia 最初是在 2019 年 11 月左右重新设计使用 Composition API 。从那时起,最初的原则仍然相同,但 Pinia 对 Vue 2 和 Vue 3 都有效,并且不需要您使用组合 API。