尤大的 Vue3.0 已经发布有一阵子了,已经很成熟了。
而且 Element Plus + Vite 也出了一段时间了,是时候该上手体验分享一波了。
主要是要熟练一下 Vue3,好准备用 Vue3 重构一下自己的网站项目: blog-vue-typescript ,计划是过年期间会着手重构这个项目,年后会上线。
全局安装 vite-app
npm i -g vite-app
创建项目
yarn create vite-app <project-name> # 或者 npm init vite-app <project-name>
进入项目,安装依赖
cd <project-name> yarn # 或 npm i
运行项目
yarn dev
打开浏览器 http://localhost:3000 查看
加入 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' }
yarn add vue-router@next vuex@next
在根目录下创建 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/
和 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。
至此,一个基于 Vue3 全家桶 + Vite + TypeScript + Eslint + Element Plus 的开发环境已经搭建完毕,现在就可以编写代码了。
各个组件的使用方法请参阅它们各自的文档。
不得不说 Vue3 + Element Plus + Vite + TypeScript 是真的香!
推荐一个 Vue3 相关的资料汇总: Vue3 的学习教程汇总、源码解释项目、支持的 UI 组件库、优质实战项目,相信你会挖到矿哦!
Vue3 中文文档,国内 CDN 加速版:
https://vue3js.cn/docs/zh/
Element Plus 官网:
https://element-plus.org/#/zh-CN
作为 2021 第 2 篇原创技术文章,质量应该还可以吧,1 月的 KPI 完成,哈哈哈 😅
猫哥的年终总结在这里: 前端工程师的 2020 年终总结 - 乾坤未定,你我皆黑马,希望能带给你一点启发,也看看猫哥的脸都被打歪的 😂
参考文章:vue3 + vite + typescript + eslint + jest 项目配置实践
推荐阅读