Project technology stack
- Build tool: [email protected]
- Programming language: TypeScript @4.4.4
- Front-end framework: [email protected]
- CSS precompilation: sASS
- Request tool: Axios
- Eslint +prettier+stylelint
Project structures,
Initialize the project
First create an empty folder and execute in that folder
npm init -y
Copy the code
Webpack dependency installation
npm i webpack webpack-cli webpack-merge webpack-dev-server -D
Copy the code
Vue dependent installation
npm i vue@next vue-router@next vuex@next
Copy the code
The versions completed by the author are as follows:
- Vue: “3.2.23”
- Vue – the router: “4.0.12”
- Vuex: “4.0.2”
TS dependent installation
npm i typescript -D
Copy the code
And create tsconfig. Json
{
"compilerOptions": {
"target": "es5".// Specify the compiled ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'
"module": "esnext".// Specify the module standard to use: 'none', 'commonJS ',' AMD ', 'system', 'umD ',' ES2015 ', or 'ESNext'
"strict": true.// Enable all strict type checking options.
"jsx": "preserve".// Specify the development environment for JSX code: 'preserve', 'react-native', or 'react'
"importHelpers": true.// Import helper functions from tslib (such as __extends, __rest, etc.)
"moduleResolution": "node".// Select the module resolution strategy, there are two types 'node' and 'classic'.
"experimentalDecorators": true.// List of module names to baseUrl pathmaps.
"skipLibCheck": true.// Ignore type checking for all declaration files (*.d.ts).
"esModuleInterop": true.// Support the use of import D from 'CJS' in CommonJs modules to address TypeScript processing of CommonJs/AMD/UMD modules in the same way as ES6 modules
"allowSyntheticDefaultImports": true.// Allow default imports from modules that do not have default exports set. This does not affect the output of the code, just for type checking.
"sourceMap": true.// Generate the corresponding.map file.
"baseUrl": ".".// Resolve the base directory of non-relative module names. Relative modules are not affected by baseUrl
"paths": {
// Used to set the module name to baseUrl based path mapping
"@ / *": ["src/*"]},"lib": ["esnext"."dom"."dom.iterable"."scripthost"] // lib is used to specify the library files to be included in the compilation
},
"include": [
"src/**/*.ts"."src/**/*.tsx"."src/**/*.vue"."tests/**/*.ts"."tests/**/*.tsx"."types/**/*.d.ts"."types/*.d.ts"].// Specify a list of paths to compile, but the difference with files is that the paths can be folders or files, relative or absolute paths can be used, and wildcards can be used, such as "./ SRC "to compile all files in the SRC folder and files in subfolders
"exclude": ["node_modules"] // exclude specifies the files to exclude and not compile. It also specifies a list. Like include, it can be a file or folder, a relative path or an absolute path
}
Copy the code
HTML integration depends on installation
npm i html-webpack-plugin -D
Copy the code
Create entry file
1. Create a public directory under this folder and create index.html under this directory
<! DOCTYPEhtml>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<link rel="shortcut icon" href="/favicon.ico" />
<title>webpack5+ts+vue3</title>
<meta
name="description"
content=Webpack5 + TS + VUE3 Construction Project
/>
<meta
name="keyword"
content="vue3 kai"
/>
</head>
<body>
<div id="app"></div>
</body>
</html>
Copy the code
2. Create a SRC directory under the file
Create the app.vue file
<template>
<router-view />
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "App",
});
</script>
Copy the code
And create the index.ts file
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
Copy the code
Create the WebPack configuration file
Create a build folder in the build folder, and create three files webpack.base.conf.js, webpack.dev.js, and webpack.prod.js in the build folder, write the basic configuration file, and gradually improve later
-
Webpack. Base. Conf. Js file
const path = require("path"); function resolve(dir) { return path.join(__dirname, "..", dir); } const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: { app: resolve("src/index.ts"),},resolve: { extensions: [".js".".vue".".json".".ts".".tsx".".mjs"].alias: { "@": resolve("src"),}},module: { rules: [{test: /\.vue$/, use: [ { loader: "vue-loader"],},include: /(src)/,}]},plugins: [ / / the vue - loader plug-in new vueLoader.VueLoaderPlugin(), new HtmlWebpackPlugin({ filename: "index.html".template: resolve("public/index.html"), favicon: resolve("public/favicon.ico"), inject: true,})]};Copy the code
-
webpack.dev.js
const { merge } = require("webpack-merge"); const webpack = require("webpack"); const common = require("./webpack.base.conf"); const path = require("path"); function resolve(dir) { return path.join(__dirname, "..", dir); } const devWebpackConfig = merge(common, { mode: "development".devtool: "eval-cheap-module-source-map".module: { rules: []},output: { path: resolve("dist"), filename: "js/[name].[hash].js".chunkFilename: "js/[name].[hash].js".publicPath: "/",},// Log printing only prints errors and warnings stats: "errors-warnings".devServer: { host: "0.0.0.0".historyApiFallback: { rewrites: [{from: /.*/g, to: "/index.html",}]},allowedHosts: "all".port: 8080./ / the port number open: false.// Automatically open hot: true./ / hot update client: { progress: true.// Outputs the running progress to the console. overlay: { warnings: false.errors: true }, // Display error messages in full screen }, compress: true.// Enable gzip compression for all services proxy: { "/api": { target: "...".changeOrigin: true.// Whether the request is cross-domain pathRewrite: { "^/api": "",},},},},plugins: [ new webpack.DefinePlugin({ "process.env.NODE_ENV": "'development'".__VUE_OPTIONS_API__: true.__VUE_PROD_DEVTOOLS__: false,})]});module.exports = devWebpackConfig; Copy the code
-
webpack.prod.js
const { merge } = require("webpack-merge"); const webpack = require("webpack"); const common = require("./webpack.base.conf"); const path = require("path"); function resolve(dir) { return path.join(__dirname, "..", dir); } module.exports = function (env, argv) { const nodeEnv = env.dev ? "development" : env.test ? "test" : "production"; return merge(common, { mode: "production".devtool: "source-map".module: { rules: []},plugins: [ new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(nodeEnv), __VUE_OPTIONS_API__: true.__VUE_PROD_DEVTOOLS__: false,})],output: { path: resolve("dist"), filename: "js/[name].[hash].js".chunkFilename: "js/[name].[hash].js",}}); };Copy the code
Then modify the scripts configuration in the package.json file
"scripts": {
"dev": "webpack serve --config build/webpack.dev.js"."build:dev": "webpack --env dev --config build/webpack.prod.js"."build:test": "webpack --env test --config build/webpack.prod.js"."build:prod": "webpack --env prod --config build/webpack.prod.js"
},
Copy the code
Style specific configuration
npm i css-loader sass-loader sass postcss postcss-loader postcss-preset-env vue-style-loader -D
Copy the code
Corresponding to webPack configuration
module: {
rules: [{test: /\.(sa|sc|c)ss$/,
use: [
'vue-style-loader',
{
loader: 'css-loader'.options: {
sourceMap: false,}}, {loader: 'postcss-loader'.options: {
postcssOptions: {
// postCSs-preset -env integrates autoprefixer to add CSS third-party prefixes
plugins: ['postcss-preset-env'],},},}, {loader: 'sass-loader'.options: {
additionalData: ` @use "@/styles/variables.scss" as *; // Import the sass variable @use "@/styles/mixin. SCSS "as *; // Global import sass mixed with ',},},],}Copy the code
File configuration, such as images and fonts
Webpack5 has a built-in asset module that can handle static resources instead of file-loader, url-loader, and raw-loader, much simpler than before
rules: [
{
test: /\.(png|jpe? g|gif|svg)(\? . *)? $/,
type: 'asset'.parser: {
dataUrlCondition: {
maxSize: 10 * 1024,}},generator: {
filename: 'images/[base]',},exclude: [resolve('src/assets/svg')]}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/,
type: 'asset'.generator: {
filename: 'files/[base]',}}, {test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/,
type: 'asset'.generator: {
filename: 'media/[base]',}},],Copy the code
Babel configuration
npm i @babel/cli @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/preset-typescript babel-loader -D
Copy the code
npm i @babel/runtime-corejs3 core-js
Copy the code
-
@babel/cli: Babel comes with a built-in CLI command line tool, which can be compiled through the command line (such as NPX Babel script.js) file, debugging, can be installed or not installed
-
@babel/core: Bable core NPM package for transcoding
-
@ Babel/plugin – transform – runtime and @ Babel/runtime – corejs3:
The js file is transcoded by Babel to generate many helper functions (possibly a lot of duplicate functions). Polyfill will mount the missing function of the target browser on the global variable. The plugin will import both helper and Polyfill from one unified place instead. And the introduced objects and global variables are completely isolated
The @babel/plugin-transform-runtime package is used to translate code, which may import modules from @babel/runtime-corejs3, so the former runs at compile time and the latter at runtime.
-
@babel/preset-env: Preset kit, which contains every possible translation tool.
-
@babel/preset-typescript: Parses typescript’s Babel preset
-
Babel-loader: // Webpack loader, which relies on @babel/core, is an intermediate bridge that calls apis in Babel /core to tell Webpack what to do with JS
-
Core-js: The JavaScript standard library’s polyfill, @babel/preset-env reference package
let babelLoaderConf = {
loader: 'babel-loader'.options: {
presets: [['@babel/preset-env',
{
targets: {
browsers: ['ie>=8'.'chrome>=62'].node: '8.9.0',},// Switch the target
debug: false.// Whether to print which plug-ins prese-env uses for the current configuration and the collection of browsers we support
useBuiltIns: 'usage'.// Import on demand
corejs: '3.0'./ / corejs version},], ['@babel/preset-typescript',
{
allExtensions: true.All file extensions are supported, otherwise using ts in vue files will cause an error},]].plugins: [['@babel/plugin-transform-runtime',
{
corejs: 3,},],],},};// webpack
rules: [
{
test: /\.(ts|js)x? $/,
use: [babelLoaderConf],
exclude: /node_modules/],},Copy the code
Babel7 now has the ability to parse typescript, eliminating the need for TS-Loader
Vue file Webpack configuration
Since it is a vue3 project, vuE-Loader needs to install the latest version, and the version installed by the author is 16.8.3
npm i vue-loader@next @vue/compiler-sfc -D
Copy the code
Webpack configuration
const vueLoader = require('vue-loader')...module.exports = {
...
module: {
rules: [{test: /\.vue$/,
use: [
{
loader: 'vue-loader'],},include: /(src)/,}]},plugins: [
/ / the vue - loader plug-in
new vueLoader.VueLoaderPlugin(),
],
}
Copy the code
Add the corresponding vUE type declaration to prevent type errors
Create a new Types folder and add the shims-vue.d.ts file
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
Copy the code
Code specification
Eslint + prettier + stylelint, plus.EditorConfig file
.editorconfig
The.editorConfig file can unify the specification for editing, such as indentation Spaces and so on
/ /. Editorconfig file
root = true[*. {js, JSX, ts, the TSX, vue, json, HTML, CSS, SCSS}] indent_style = space # indented style (TAB | space) indent_size =2# # the indentation size end_of_line = CRLF control line type (lf | cr | CRLF) insert_final_newline =trueAlways insert a new line at the end of the fileCopy the code
eslint
Used to check whether the code conforms to the code specification, code syntax logic, code format and so on
npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-prettier eslint-config-prettier eslint-plugin-vueCopy the code
- Eslint: The core code for ESLint
- @typescript-eslint/parser: A parser for ESLint that parses typescript to check and standardize typescript code
- @typescript-eslint/eslint-plugin: This is an ESLint plugin that contains various specifications for checking typescript code
- Eslint-plugin-prettier, eslint-config-prettier: EsLint is compatible with prettier
- Eslint-plugin-vue: VUE rules are provided for vUE, including vue-eslint-parser, so there is no need to install vue-eslint-parser manually
. Eslintrc. Js file
module.exports = {
root: true.env: {
node: true.browser: true,},parserOptions: {
parser: "@typescript-eslint/parser".// Parse the script tag in the.vue file
sourceType: "module".ecmaVersion: 12,},plugins: ["vue"."@typescript-eslint"].extends: [
"plugin:vue/recommended"."plugin:prettier/recommended"."prettier/@typescript-eslint"."plugin:@typescript-eslint/recommended",].rules: {
indent: ["warn".2].// Indent style}};Copy the code
prettier
Beautify the code
npm i prettier -D
Copy the code
. Prettierrc. Js file
module.exports = {
singleQuote: true.// Use single quotes
tabWidth: 2.// The length of the Tab indent
endOfLine: 'auto'.// The form of a newline at the end of the file
semi: false.// No semicolons
};
Copy the code
stylelint
npm i stylelint stylelint-config-prettier stylelint-config-recess-order stylelint-config-standard stylelint-order stylelint-scss -D
Copy the code
Add the stylelint.config.js file in the root directory
module.exports = {
defaultSeverity: 'warn',
extends: [
'stylelint-config-standard',
'stylelint-config-recess-order', // Attribute collation
],
plugins: ['stylelint-scss', 'stylelint-order'],
rules: {
'no-invalid-double-slash-comments': null.// Allow double slash comments
'custom-property-no-missing-var-function': null,
'no-empty-source': null,
'selector-class-pattern': null,
'alpha-value-notation': null.// Allow decimals
'color-function-notation': null.// RGB colors are allowed
'media-feature-name-no-vendor-prefix': true.// Do not allow prefixes of media feature names, plug-ins add them automatically
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['deep'], // Ignore the deep pseudo-element
},
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'mixin',
'include',
'if',
'else',
'extend',
'for',
'$',
'forward',
'use',
], // Ignore rules},],}},Copy the code
UI Framework introduction (Element-Plus)
The UI framework used in this project is Element-Plus, because the current framework is all in beta version. After many mistakes, the current version is 1.0-beta.24. Check the official document of the framework. Introduce the use of the unplugin-vue-Components plug-in as needed
NPM I [email protected] unplugin - vue - componentsCopy the code
According to the need to introduce
Configuration webpack
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')...module.exports = {
...
plugins: [...// Element-plus is introduced on demand
Components({
resolvers: [
ElementPlusResolver({
importStyle: false.// Do not introduce a style file for later themetization}),],}),],};Copy the code
The loader for MJS in webPack configuration is also required
Changed fullySpecified to false so that the element-Plus Module can be imported without an extension to avoid Module Not found errors and compile failures
module.exports = {
module: {
rules: [
// element-plus
{
test: /\.mjs$/,
include: /node_modules/,
resolve: {
fullySpecified: false,},type: 'javascript/auto',},],},}Copy the code
Add import TS files on demand
// element-plus.ts
import type { App } from 'vue'
import {
ElButton,
ElSelect,
...
} from 'element-plus'
const components = [
ElButton,
ElSelect,
...
]
const option = {
size: 'medium',}export default function introduceElement(app: App) :void {
components.forEach((component) = > {
app.use(component)
})
app.config.globalProperties.$ELEMENT = option
}
Copy the code
And import the file into the entry file
// index.ts
import introduceElement from '@/utils/vue/element-plus'
const app = createApp(App)
// Introduce element UI components on demand
introduceElement(app)
Copy the code
themed
Create a new theme. SCSS file
@forward "element-plus/theme-chalk/src/common/var.scss" with (
$colors: (
"primary": (
"base": #016EFD,),"success": (
"base": #016EFD,),"warning": (
"base": #faad14,),"danger": (
"base": #f56c6c,),"error": (
"base": #f56c6c,),"info": (
"base": #0076ff,),),$font-path : '~element-plus/dist/fonts' ,
$button-padding-horizontal: (
"default": 80px));Copy the code
Create a new elementPlus.scss file
@use './theme.scss' as *;
@use 'element-plus/theme-chalk/src/index.scss' as *;
Copy the code
And imported in the entry file index.ts
import './styles/elementPlus.scss'
Copy the code
At this point, theming was complete, and several versions of Element-Plus were tried before theming worked without error
Project optimization
Package volume analysis
Optimize by analyzing the size of each bundle.
npm i -D webpack-bundle-analyzer
Copy the code
We can do this by adding scripts to package.json
"build:analyzer": "webpack --env analyzer --config build/webpack.prod.js"
Copy the code
Webpack configuration file
function resolve(dir) {
return path.join(__dirname, "..", dir);
};
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; .module.exports = function (env, argv) {
const analyzerPlugins = env.analyzer
? [
new BundleAnalyzerPlugin({
analyzerMode: "static".openAnalyzer: false.// generateStatsFile: true,
reportFilename: resolve("./report/report.html"),
statsFilename: resolve("./report/stats.json"}),] : [];return merge(common, {
mode: "production".plugins: [
...
...analyzerPlugins,
],
});
};
Copy the code
The final package size results are printed to the report folder in the root directory
Added compilation progress bar to optimize compilation prompt
npm i -D progress-bar-webpack-plugin friendly-errors-webpack-plugin
Copy the code
- Progress-bar-webpack-plugin: progress bar plugin
- Friendly-errors-webpack-plugin: optimizes the prompt plug-in
const { merge } = require('webpack-merge')
const webpack = require('webpack')
const { resolve } = require('./utils.js')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
const common = require('./webpack.base.conf')
const chalk = require('chalk')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const devWebpackConfig = merge(common, {
// Log printing only prints errors and warnings
stats: 'errors-warnings'. }) devWebpackConfig.plugins.push(/ / the progress bar
new ProgressBarPlugin({
format: ` :msg [:bar] ${chalk.green.bold(':percent')} (:elapsed s)`.clear: true,}).// Error message
new FriendlyErrorsWebpackPlugin({
// Output on success
compilationSuccessInfo: {
messages: [
`Your application is running here: http://${devWebpackConfig.devServer.host}:${devWebpackConfig.devServer.port}`,]},// Whether to empty the console every time
clearConsole: true,}))module.exports = devWebpackConfig
Copy the code
Cache cache
Configure webPack persistent cache cache: Filesystem to cache webpack modules and chunks to improve the build speed.
Using cache: Filesystem can cache webPack templates for the build process to speed up the second build.
function resolve(dir) {
return path.join(__dirname, "..", dir);
};
const devWebpackConfig = merge(common, {
...
/ / cache
cache: {
type: 'filesystem'.buildDependencies: {
config: [__filename], // The array object that the extra code depends on for the build. Webpack will use the hash values of these items and all dependencies to invalidate the file system cache.
},
cacheDirectory: resolve('temp_cache'),
name: 'scf-cache'./ / path temp_cache/SCF - cache
compression: 'gzip',}})Copy the code
Clear the dist folder
npm i clean-webpack-plugin -D
Copy the code
This plug-in can clean up old files (the results of the last build) under DIST at package time to ensure that the build results in the Dist folder are up to date.
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
plugins: [
/ / clear the dist
new CleanWebpackPlugin(),
]
}
Copy the code
CSS separation
npm i mini-css-extract-plugin -D
Copy the code
The mini-CSS-extract-plugin can extract CSS into a separate file, create a CSS file for each JS file containing CSS, and support on-demand loading of CSS and SourceMaps.
Extracting CSS files for configuration also prevents large files packaged in JS and network requests timeouts because of large files.
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = function (env, argv) {
module: {
rules: [{test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader'}, {loader: 'postcss-loader'}, {loader: 'sass-loader',},],},},plugins: [
/ / CSS
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css'.chunkFilename: 'css/[name].[contenthash].css',}),],})}Copy the code
CSS compression
npm i css-minimizer-webpack-plugin -D
Copy the code
Css-minimizer-webpack-plugin can optimize and compress CSS files.
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
plugins: [
new CssMinimizerPlugin(),
],
};
Copy the code
SVG Sprites figure
npm i svg-sprite-loader -D
Copy the code
Modify the WebPack configuration to configure Sprites diagrams for SVG images in the specified directory (SRC/Assets/SVG)
module.exports = {
module: {
rules: [{test: /\.svg$/,
loader: 'svg-sprite-loader'.include: [resolve('src/assets/svg')].options: {
symbolId: 'icon-[name]',}}, {test: /\.(png|jpe? g|gif|svg)(\? . *)? $/,
type: 'asset'.parser: {
dataUrlCondition: {
maxSize: 10 * 1024,}},generator: {
filename: 'images/[base]',},exclude: [resolve('src/assets/svg'],},],},}Copy the code
Plug-in SVG component
<template> <svg :class="svgClass" aria-hidden="true"> <use :xlink:href="iconName"></use> </svg> </template> <script> export default { name: 'SvgIcon', props: { iconClass: { type: String, required: true, }, className: { type: String, }, }, computed: { iconName() { return `#icon-${this.iconClass}` }, svgClass() { if (this.className) { return 'svg-icon ' + this.className } else { return 'svg-icon' } }, }, } </script> <style scoped> .svg-icon { width: 1em; height: 1em; Vertical - align: 0.15 em. fill: currentColor; overflow: hidden; } </style>Copy the code
Create ts file and import SVG uniformly
import type { App } from 'vue'
import SvgIcon from '@/components/common/svg/svg-icon.vue' / / SVG components
const requireAll = (requireContext: any) = >
requireContext.keys().map(requireContext)
const req = require.context('@/assets/svg'.false./\.svg$/)
requireAll(req)
export default function svgIconRegistered(app: App) :void {
app.component('SvgIcon', SvgIcon)
}
Copy the code
In the entry file index.ts
import { createApp } from 'vue'
import App from './App.vue'
import svgIconRegistered from '@/utils/vue/svg-component'
const app = createApp(App)
/ / SVG Sprites
svgIconRegistered(app)
Copy the code
For example, menu-icon is the file name of an SVG diagram
<svg-icon icon-class="menu-icon" class="menu-icon" />
Copy the code
Check and optimize before Git commit
Integrating HusKY and Lint-staged, Husky can help you block bad code commits and thrusters; Lint-staged files can be checked only once at a time.
npm i lint-staged husky -D
Copy the code
Run after installation
npx husky install
Copy the code
When you run it, you’ll notice that the project root directory generates a.husky folder. Create a new pre-commit file in that directory
// pre-commit file #! /bin/sh . "$(dirname "$0")/_/husky.sh" npx lint-stagedCopy the code
Then create a new lint-lanterns.config.js file in the root directory,
module.exports = {
'src/**/*.{js,vue,png,svg,jpg,jepg,gif}': [
(filenames) = >
filenames.map(
(filename) = > `prettier --write --ignore-unknown '${filename}'`),],}Copy the code
This will prettier the submission before it is submitted
The project address
Github.com/chenkai77/w…