五一期間,花了 3 天時間,邊學 Vue3 和 Vite2,邊重構本身的項目,終於都用 Vue3 + TypeScript + Vite2 + Vuex4 + Vue-Router4 + element-plus 重構完啦!css
終於完成一項心心念唸的 2021 年度目標了 ✌️html
項目地址:前端
https://github.com/biaochenxuying/blog-vue-typescript
效果圖:vue
完整效果請看:node
https://biaochenxuying.cn
⬆️ 返回頂部webpack
全部技術都是當前最新的。ios
用 vite-app 建立項目git
yarn create vite-app <project-name> # 或者 npm init vite-app <project-name>
而後按照提示操做便可!github
進入項目,安裝依賴web
cd <project-name> yarn # 或 npm i
運行項目
yarn dev
打開瀏覽器 http://localhost:3000 查看
在建立項目的時候能夠 TypeScript 的,若是你選擇了 TypeScript ,能夠忽略第 2 個步驟。
加入 ts 依賴
yarn add --dev typescript
在 項目根目錄下建立 TypeScript 的配置文件 tsconfig.json
{ "compilerOptions": { // 容許從沒有設置默認導出的模塊中默認導入。這並不影響代碼的輸出,僅爲了類型檢查。 "allowSyntheticDefaultImports": true, // 解析非相對模塊名的基準目錄 "baseUrl": ".", "esModuleInterop": true, // 從 tslib 導入輔助工具函數(好比 __extends, __rest等) "importHelpers": true, // 指定生成哪一個模塊系統代碼 "module": "esnext", // 決定如何處理模塊。 "moduleResolution": "node", // 啓用全部嚴格類型檢查選項。 // 啓用 --strict至關於啓用 --noImplicitAny, --noImplicitThis, --alwaysStrict, // --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。 "strict": true, // 生成相應的 .map文件。 "sourceMap": true, // 忽略全部的聲明文件( *.d.ts)的類型檢查。 "skipLibCheck": true, // 指定ECMAScript目標版本 "target": "esnext", // 要包含的類型聲明文件名列表 "types": [ ], "isolatedModules": true, // 模塊名到基於 baseUrl的路徑映射的列表。 "paths": { "@/*": [ "src/*" ] }, // 編譯過程當中須要引入的庫文件的列表。 "lib": [ "ESNext", "DOM", "DOM.Iterable", "ScriptHost" ] }, "include": [ "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx" ], "exclude": [ "node_modules" ] }
在 src 目錄下新加 shim.d.ts 文件
/* eslint-disable */ import type { DefineComponent } from 'vue' declare module '*.vue' { const component: DefineComponent<{}, {}, any> export default component }
把 main.js 修改爲 main.ts
在根目錄,打開 Index.html
<script type="module" src="/src/main.js"></script> 修改成: <script type="module" src="/src/main.ts"></script>
安裝 eslint prettier 依賴
@typescript-eslint/parser @typescr ipt-eslint/eslint-plugin
爲 eslint 對 typescript 支持。
yarn add --dev eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/parser @typescr ipt-eslint/eslint-plugin
在根目錄下創建 eslint 配置文件: .eslintrc.js
module.exports = { parser: 'vue-eslint-parser', parserOptions: { parser: '@typescript-eslint/parser', ecmaVersion: 2020, sourceType: 'module', ecmaFeatures: { jsx: true } }, extends: [ 'plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', 'prettier/@typescript-eslint', 'plugin:prettier/recommended' ], rules: { '@typescript-eslint/ban-ts-ignore': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/no-empty-function': 'off', 'vue/custom-event-name-casing': 'off', 'no-use-before-define': 'off', // 'no-use-before-define': [ // 'error', // { // functions: false, // classes: true, // }, // ], '@typescript-eslint/no-use-before-define': 'off', // '@typescript-eslint/no-use-before-define': [ // 'error', // { // functions: false, // classes: true, // }, // ], '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/ban-types': 'off', '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^h$', varsIgnorePattern: '^h$' } ], 'no-unused-vars': [ 'error', { argsIgnorePattern: '^h$', varsIgnorePattern: '^h$' } ], 'space-before-function-paren': 'off', quotes: ['error', 'single'], 'comma-dangle': ['error', 'never'] } };
創建 prettier.config.js
module.exports = { printWidth: 100, tabWidth: 2, useTabs: false, semi: false, // 未尾逗號 vueIndentScriptAndStyle: true, singleQuote: true, // 單引號 quoteProps: 'as-needed', bracketSpacing: true, trailingComma: 'none', // 未尾分號 jsxBracketSameLine: false, jsxSingleQuote: false, arrowParens: 'always', insertPragma: false, requirePragma: false, proseWrap: 'never', htmlWhitespaceSensitivity: 'strict', endOfLine: 'lf' }
npm install vue-router@4 vuex
在根目錄下建立 store/index.ts
import { InjectionKey } from 'vue' import { createStore, Store } from 'vuex' export interface State { count: number } export const key: InjectionKey<Store<State>> = Symbol() export const store = createStore<State>({ state() { return { count: 0 } }, mutations: { increment(state) { state.count++ } } })
main.ts 修改
import { createApp } from 'vue' import { store, key } from './store' import App from './App' import './index.css' const app = createApp(App) app.use(store, key) app.mount('#app')
components/HelloWord.vue 修改
<template> <h1>{{ msg }}</h1> <button @click="inCrement"> count is: </button> <p>{{ count }}</p> </template> <script> import { defineComponent, computed } from 'vue' import { useStore } from 'vuex' import { key } from '../store' export default defineComponent({ name: 'HelloWorld', props: { msg: { type: String, default: '' } }, setup() { const store = useStore(key) const count = computed(() => store.state.count) return { count, inCrement: () => store.commit('increment') } } }) </script>
在 src 目錄下創建 router/index.ts,內容以下:
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"; import HelloWorld from "../components/HelloWorld.vue"; const routes: Array<RouteRecordRaw> = [ { path: "/", name: "HelloWorld", component: HelloWorld, }, { path: "/about", name: "About", // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "About" */ "../components/About.vue") } ]; const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes, }); export default router;
再新建一個 components/About.vue 文件,內容以下:
<template> <img alt="Vue logo" src="../assets/logo.png" /> <h1>{{ msg }}</h1> </template> <script lang="ts"> import { defineComponent } from 'vue' export default defineComponent({ name: 'About', data() { return { msg: 'Hello Vue 3.0 + Vite!' } }, setup() {} }) </script>
再修改 main.ts
import { createApp } from 'vue' import { store, key } from './store' import router from "./router"; import App from './App' import './index.css' const app = createApp(App) app.use(store, key) app.use(router) app.mount('#app')
和 http://localhost:3000/about 便可
全局安裝
npm install element-plus --save
你能夠引入整個 Element Plus,或是根據須要僅引入部分組件。咱們先介紹如何引入完整的 Element。
完整引入
在 main.js 中寫入如下內容:
import { createApp } from 'vue' import ElementPlus from 'element-plus'; import router from "./router"; import 'element-plus/lib/theme-chalk/index.css'; import App from './App.vue'; import './index.css' const app = createApp(App) app.use(ElementPlus) app.use(router) app.mount('#app')
以上代碼便完成了 Element Plus 的引入。須要注意的是,樣式文件須要單獨引入。
按需引入
藉助 babel-plugin-component,咱們能夠只引入須要的組件,以達到減少項目體積的目的。
首先,安裝 babel-plugin-component:
npm install babel-plugin-component -D
而後,將 .babelrc 修改成:
{ "plugins": [ [ "component", { "libraryName": "element-plus", "styleLibraryName": "theme-chalk" } ] ] }
接下來,若是你只但願引入部分組件,好比 Button 和 Select,那麼須要在 main.js 中寫入如下內容:
import { createApp } from 'vue' import { store, key } from './store'; import router from "./router"; import { ElButton, ElSelect } from 'element-plus'; import App from './App.vue'; import './index.css' const app = createApp(App) app.component(ElButton.name, ElButton); app.component(ElSelect.name, ElSelect); /* or * app.use(ElButton) * app.use(ElSelect) */ app.use(store, key) app.use(router) app.mount('#app') app.mount('#app')
更詳細的安裝方法請看 快速上手。
在引入 Element Plus 時,能夠傳入一個全局配置對象。
該對象目前支持 size
與 zIndex
字段。size
用於改變組件的默認尺寸,zIndex
設置彈框的初始 z-index(默認值:2000)。按照引入 Element Plus 的方式,具體操做以下:
完整引入 Element:
import { createApp } from 'vue' import ElementPlus from 'element-plus'; import App from './App.vue'; const app = createApp(App) app.use(ElementPlus, { size: 'small', zIndex: 3000 });
按需引入 Element:
import { createApp } from 'vue' import { ElButton } from 'element-plus'; import App from './App.vue'; const app = createApp(App) app.config.globalProperties.$ELEMENT = option app.use(ElButton);
按照以上設置,項目中全部擁有 size
屬性的組件的默認尺寸均爲 'small',彈框的初始 z-index 爲 3000。
其中 proxy 和 alias 是和 vue-cli 區別比較大的地方。
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import styleImport from 'vite-plugin-style-import' import path from 'path' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), styleImport({ libs: [ { libraryName: 'element-plus', esModule: true, ensureStyleFile: true, resolveStyle: (name) => { return `element-plus/lib/theme-chalk/${name}.css`; }, resolveComponent: (name) => { return `element-plus/lib/${name}`; }, } ] }) ], /** * 在生產中服務時的基本公共路徑。 * @default '/' */ base: './', /** * 與「根」相關的目錄,構建輸出將放在其中。若是目錄存在,它將在構建以前被刪除。 * @default 'dist' */ // outDir: 'dist', server: { // hostname: '0.0.0.0', host: "localhost", port: 3001, // // 是否自動在瀏覽器打開 // open: true, // // 是否開啓 https // https: false, // // 服務端渲染 // ssr: false, proxy: { '/api': { target: 'http://localhost:3333/', changeOrigin: true, ws: true, rewrite: (pathStr) => pathStr.replace('/api', '') }, }, }, resolve: { // 導入文件夾別名 alias: { '@': path.resolve(__dirname, './src'), views: path.resolve(__dirname, './src/views'), components: path.resolve(__dirname, './src/components'), utils: path.resolve(__dirname, './src/utils'), less: path.resolve(__dirname, "./src/less"), assets: path.resolve(__dirname, "./src/assets"), com: path.resolve(__dirname, "./src/components"), store: path.resolve(__dirname, "./src/store"), mixins: path.resolve(__dirname, "./src/mixins") }, } })
在 npm run dev
打包時不報錯,可是在 npm run build
時卻報錯了,build 的時候會把 node_modules
裏面的文件也編譯,因此挺多 element-plus 的類型文件報錯了。
把 tsconfig.json
裏面的 include
和 exclude
修改一下就不會了,配置以下
{ "compilerOptions": { "target": "esnext", "module": "esnext", "moduleResolution": "node", "strict": true, "jsx": "preserve", "sourceMap": true, // 忽略 this 的類型檢查, Raise error on this expressions with an implied any type. "noImplicitThis": false, "resolveJsonModule": true, "esModuleInterop": true, "lib": ["esnext", "dom"], "types": ["vite/client"] }, "include": ["/src/**/*.ts", "/src/**/*.d.ts", "/src/**/*.tsx", "/src/**/*.vue"], // ts 排除的文件 "exclude": ["node_modules"] }
Vue3 + vite2 打包出來的文件和原來 vue2 版的差異也挺大的,由原來 2.5M 直接變成了 1.8M ,amazing!
項目代碼大多都是 2 年前的,還有不少能夠優化的地方,此次重構的過程沒對原來的樣式和代碼作什麼改動,沒那麼多時間,加上我懶 😂
此次就升級了主要框架與相應的 ui 庫,過了一遍 Vue3 中的 API,發現不少 Vue3 中新的 API 都用不上,主要是要熟練一下 Vue3 和 Vite2 項目搭建,這假期也算有所收穫。
具體項目源碼請看:
https://github.com/biaochenxuying/blog-vue-typescript
至此,一個基於 Vue3 全家桶 + Vite2 + TypeScript + Element Plus 的開發環境已經搭建完畢,如今就能夠編寫代碼了,各個組件的使用方法請參閱它們各自的文檔。
不得不說 Vue3 + Element Plus + Vite + TypeScript 是真的香!
推薦一個 Vue3 相關的資料彙總: Vue3 的學習教程彙總、源碼解釋項目、支持的 UI 組件庫、優質實戰項目,相信你會挖到礦哦!
參考文章:vue3 + vite + typescript + eslint + jest 項目配置實踐
推薦閱讀
歡迎關注公衆號: 「全棧修煉」,回覆 「電子書」 便可以得到下面 300 本技術精華書籍哦,貓哥 wx:CB834301747 。