安裝流程
1.安裝vite腳手架
npm install -g create-vite-app
# or
yarn add -g create-vite-app
2.項目初始化
yarn create vite-app <project-name>
3.集成TS
yarn add --dev typescript
4.項目根目錄創建配置文件:tsconfig.json:
{
"include": ["./**/*.ts"],
"compilerOptions": {
"jsx": "react",
"target": "es2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
// "lib": ["es2017.object"] /* Specify library files to be included in the compilation. */,
// "declaration": true /* Generates corresponding '.d.ts' file. */,
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
"sourceMap": true /* Generates corresponding '.map' file. */,
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist" /* Redirect output structure to the directory. */,
"strict": true /* Enable all strict type-checking options. */,
"noUnusedLocals": true /* Report errors on unused locals. */,
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
}
}
5.集成eslint(如果不想安裝,可以忽略5,6)
yarn add --dev eslint eslint-plugin-vue
6.項目根目錄創建配置文件.eslintrc.js:
module.exports = {
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
ecmaFeatures: {
// tsx: true, // Allows for the parsing of JSX
jsx: true,
},
},
// settings: {
// tsx: {
// version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
// }
// },
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
],
rules: {
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
},
};
7.集成pritter
yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier
8.項目根目錄創建配置文件:.prettierrc.js:
module.exports = {
semi: true,
trailingComma: "all",
singleQuote: true,
printWidth: 100,
tabWidth: 2,
endOfLine:"auto"
};
到這一步,一個Vue3+TSX的項目就搭建起來了,以上配置文件的具體內容就不做解釋了。
9.修改入口文件
因為默認項目模板是以src/main.js為入口的,我們需要把它修改為src/main.ts。
在根目錄的index.html中修改入口文件的引用即可:
... ...
<body>
... ...
<script type="module" src="/src/main.ts"></script>
</body>
</html>
10.優化TS類型推斷
在src目錄下,創建shim.d.ts、source.d.ts
shim.d.ts: (這個其實不太需要,因為項目中全是通過tsx開發的)
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}
source.d.ts: (優化編譯器提示,聲明靜態資源文件)
declare const React: string;
declare module '*.json';
declare module '*.png';
declare module '*.jpg';
11.集成vue-router
yarn add --dev vue-router@4.0.0-beta.2
這里可以去npm官網查找最新版本
在src目錄下,新建router文件夾,并在文件夾內創建index.ts index.ts:
import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
component: import('../views/Home'),
},
{
path: '/about',
name: 'About',
component: () => import('../views/About'),
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
這里創建router的方式與之前不同,在vue3中,結合TS的類型推斷,開發效率會高很多。
12.集成vuex
yarn add --dev vuex@4.0.0-beta.4
在src目錄下,新建store文件夾,并在文件夾內創建index.ts
index.ts:
import { state } from './state';
import { createStore } from 'vuex';
export default createStore({
state,
mutations: {},
actions: {},
modules: {},
});
state.js:
export interface State {
title: string;
}
export const state: State = {
title: 'Vue(v3) 與 tsx 的結合~',
};
13.main.ts
最終main.ts中引入store、router:
import { createApp } from 'vue';
import Root from './App.vue';
import router from './router';
import store from './store';
const app = createApp(Root);
app.use(router);
app.use(store);
app.mount('#app');
export default app;
vue3.0發生的變化
vue3現在沒有this,官方的解釋:在 setup() 內部,this 不會是該活躍實例的引用,因為 setup() 是在解析其它組件選項之前被調用的,所以 setup() 內部的 this 的行為與其它選項中的 this 完全不同。這在和其它選項式 API 一起使用 setup() 時可能會導致混淆。那么問題來了沒有this我們如何使用router,store,emit, nextTick這些方法呢?下面會有案例來說明如何使用。
1.創建方式改變
vue創建方式
老版本
var app = new Vue({
el: '#app',
data: {}
})
新版本
import { createApp } from 'vue';
createApp(Root).mount('#app')
vuex創建方式
老版本
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
新版本
import { state } from './state.js';
import { createStore } from 'vuex';
export default createStore({
state,
mutations: {},
actions: {},
modules: {},
});
//使用方式
<script type="ts">
import {} from 'vue';
import { useStore } from 'vuex';
import axios from '../../../axios/index';
export default {
name: 'NavMenu',
props: {
},
setup(props, context) {
const store = useStore();
const getMenu = () => {
const url = 'xxxx';
axios('post', url).then((res) => {
routerList.value = res;
store.commit('setRouterList', res)
});
};
return {
};
},
};
</script>
vue-router創建方式
老版本
import VueRouter from 'vue-router';
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
path: 'profile',
component: UserProfile
},
{
path: 'posts',
component: UserPosts
}
]
}
]
})
新版本
import { RouteRecordRaw, createRouter, createWebHashHistory } from 'vue-router';
const routes: RouteRecordRaw[] = [{
path: '/',
name: '首頁',
component: getComponentsPath('Layout'),
children: [{
{ path: '/', redirect: '/subassembly/base-subassembly' },
{
path: '/subassembly',
name: '組件',
component: view(),
children: [
{
path: '/subassembly/base-subassembly',
name: '基礎組件',
component: getViewPath('subassembly/baseSubassembly'),
},
{
path: '/subassembly/complexity-subassembly',
name: '復雜組件',
component: getViewPath('subassembly/complexitySubassembly'),
},
],
},
}]
}]
const router = createRouter({
history: createWebHashHistory(process.env.BASE_URL),//可以換成History 模式
routes,
});
export default router;
2.寫法的改變
新版中需要注意的點都在注釋中
老版本data函數 => 新版本setup函數
老版本
<template>
<div>{{num}}</div>
</template>
<script>
export default {
data() {
return {
num:0
};
},
}
</script>
<style lang='less' scoped>
</style>
新版本
<template>
<div>{{num}}</div>
</template>
<script>
import { ref, reactive, toRef } from 'vue';
export default {
setup() {
//vue2.x中的data函數
const num = 0; //不具備響應式特性
const cunst = ref(0); //單個屬性具備響應式特性
//如果想讓引用數據類型的值有響應式狀態就需要
//const books = reactive([ref('Vue 3 Guide')]),下面的這種寫法
const checkList = reactive([]);
//多個屬性具備響應式特性
const state = reactive({
name: '張三',
age: 18,
});
return {
num,
cunst,
//因為setup本身是返回一個對象的如果有多個返回可以使用ES6的展開運算符,
//但是reactive方法有bug對于直接把state放入return中會丟失響應式特效所以在返回之前用toRef方法包裹
//toRef:簡單來講就是讓reactive中的值每個都具有響應式特性
...toRef(state),
};
},
};
</script>
<style lang='less' scoped>
</style>
生命周期
老版本
<template>
<div>
</div>
</template>
<script>
export default {
created() {
},
mounted() {
},
methods: {
},
}
</script>
<style lang='less' scoped>
</style>
新版本
//生命周期函數,vue3.0刪除掉兩個生命周期
//beforeCreate -> use setup()
//created -> use setup()
<template>
<div></div>
</template>
<script>
import { ref, reactive, onMounted, onUpdated, onUnmounted, toRef } from 'vue';
export default {
setup() {
onMounted(() => {
console.log('mounted!');
});
onUpdated(() => {
console.log('updated!');
});
onUnmounted(() => {
console.log('unmounted!');
});
return {
};
},
};
</script>
<style lang='less' scoped>
</style>
3.注冊全局組件
老版本
import Vue from 'vue';
import content from "./content.vue";
Vue.component("my-content ", content );
新版本
import myInput from './Input/index.vue';
import myRadio from './Radio/index.vue';
import myCheckbox from './Checkbox/index.vue';
export default {
install: (Vue: any) => {
Vue.component("MyComponentInput",myInput);
Vue.component("MyComponentRadio",myRadio);
Vue.component("MyComponentCheckbox",myCheckbox);
},
};
main文件中引入
import components from './components/components';
const app = createApp(Root);
app.use(components);
app.mount('#app');
其實按照vue3的文檔上來看的話創建全局組件和vue2時沒有變化,但是我用的時候報錯,原因沒有找到,下面是vue3文檔的寫法
const app = Vue.createApp({...})
app.component('my-component-name', {
/* ... */
})
4.Vue3.0配置全局屬性的方法
老版本
Vue 2.0之前我們封裝axios和message等通用屬性的時候是這么寫的:
Vue.prototype.$api = api
Vue.prototype.$http = http
新版本
const app=createApp(App);
app.use(store);
app.use(router);
app.use(Antd);
app.mount('#app');
// 配置全局屬性
app.config.globalProperties.$message = message;
app.config.globalProperties.$http=http;
app.config.globalProperties.$api=api;
//使用方式
import { getCurrentInstance } from "vue";
在setUp函數中通過
const { ctx } = getCurrentInstance(); //獲取上下文實例,ctx=vue2的this
ctx.$api ()
//或者
const { proxy } = getCurrentInstance();
proxy.http();
vue3中使用Provide/Inject依賴注入,將替代vue2中在原型鏈上掛載一些屬性
//APP.vue
//在app.vue內注冊
import { provide ,ref ,readonly} from "vue";
setup() {
//用ref包裹可使location變為響應式
let location=ref('location')
const updataLocation=()=>{
location.value='變身后的大Location'
}
/*readonly包裹后可以在組件內引用時不被改變值。
否則在組件內可以直接通過location.value=***將值改變,
包裹后只能通過updataLocation()方法改變*/
provide('location',readonly(location))
provide('updataLocation',updataLocation)
}
//組件內使用
import { inject} from "vue";
setup(){
const userLocation = inject('location')
const updataLocation = inject('updataLocation')
return{
userLocation,updataLocation
}
}
5.vue3封裝input
//對于所有不帶參數的 v-model,請確保分別將 prop 和 event 命名更改為 modelValue 和 update:modelValue
//詳情查看https://vue-docs-next-zh-cn.netlify.app/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
<template>
<div>
<input
:class="dynamic"
:type="type"
:maxlength="maxlength"
:minlength="minlength"
:placeholder="placeholder"
:disabled="disabled"
@input="emitValue"
:value="modelValue"
/>
</div>
</template>
<script>
import { onMounted, reactive, computed, toRefs } from 'vue';
export default {
name: 'MyComponentInput',
props: {
type: {
tyep: String,
default: () => 'text',
},
maxlength: {
tyep: Number,
default: 255,
},
minlength: {
tyep: Number,
default: 0,
},
placeholder: {
tyep: String,
default: '',
},
disabled: {
tyep: Boolean,
default: false,
},
size: {
tyep: String,
default: 'small',
},
modelValue: {
tyep: String,
default: '',
},
},
setup(props, { emit }) {
const size = props.size;
const dynamic = computed(() => {
let c = '';
switch (size) {
case 'large':
c = 'large';
break;
case 'small':
c = 'small';
break;
case 'mini':
c = 'mini';
break;
}
return c;
});
onMounted(() => {
});
const emitValue = (e) => {
let value = e.target.value;
emit('update:modelValue', value);
};
return {
dynamic,
emitValue,
};
},
};
</script>
<style lang='less' scoped>
input {
background-color: #fff;
background-image: none;
border-radius: 4px;
border: 1px solid #dcdfe6;
box-sizing: border-box;
color: #606266;
display: inline-block;
font-size: inherit;
height: 40px;
line-height: 40px;
outline: none;
padding: 0 15px;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
width: 100%;
}
.large {
height: 36px;
line-height: 36px;
}
.small {
height: 32px;
line-height: 32px;
}
.mini {
height: 28px;
line-height: 28px;
}
</style>
可以看出來現在不需要在用model來設置props和event,外面也不需要在用.snyc來接值
6.如何使用路由跳轉和vue內置方法
setup函數中有兩個參數,props,context
context中有attrs,slots,emit
<template>
<div></div>
</template>
<script>
import { } from 'vue';
export default {
setup(props,context) {
return {
};
},
};
</script>
<style lang='less' scoped>
</style>
使用emit方法
<template>
<div></div>
</template>
<script>
import {} from 'vue';
export default {
setup(props, { emit }) {
emit('add', 123);
return {};
},
};
</script>
<style lang='less' scoped>
</style>
使用nextTick等方法
<template>
<div>{{ num }}</div>
</template>
<script>
import { rref, onMounted, onUpdated, onUnmounted,nextTick } from 'vue';
export default {
name: 'base-form',
setup() {
const num = ref(0);
onMounted(() => {
console.log('mounted!');
});
onUpdated(() => {
console.log('updated!');
});
onUnmounted(() => {
console.log('unmounted!');
});
nextTick(() => {
console.log('nextTick!');
})
return {
num,
};
},
};
</script>
<style lang='less' scoped>
</style>
7.在組件內如何跳轉路由
<template>
<div>
<div @click="toPage">跳轉</div>
</div>
</template>
<script>
import {} from 'vue';
import { useRouter } from 'vue-router';
export default {
setup(props, { emit }) {
const router = useRouter();
const toPage = () => {
router.push('/home');
};
return { toPage };
},
};
</script>
<style lang='less' scoped>
</style>
更多新的不同會持續更新