preface
Often when developing vUE projects, we use vuE-CLI scaffolding tools to build the project template we want. This way, we can write projects efficiently regardless of configuration. But vuE-CLI is similar to black box template configuration, for beginners, in fact, is very hurt. I spent a year or two writing business logic code and didn’t even know the basic configuration of WebPack.
Also, vuE-CLI, even if convenient, was not a perfect fit for our project, so we needed to manually build the initial project template. Lay a foundation for realizing the engineering of the project.
The project address
The project address
master
:webpack4
+express
+vue
ts-dev
:webpack4
+express
+vue
(vue-class-component
) +typescript
(This article explains)
Build file directory
` - build build file directory | | - configs project configuration | | -- appEnvs. Js global variable configuration | | -- options. Js other configuration | | -- proxy. Js service proxy configuration | | - plugin plug-in | | | - rules rules ` -- development. | js development environment configuration ` -- production. | js production configuration ` -- webpack. Base. Js based environment configuration | ` -- - public public folder SRC code directory | - @ types typescript declaration file | | - | HTTP HTTP file - assets multimedia documents - components component files | | - store state file - utils tools File | - views folder view | | - home | | -- home. The module. The module SCSS CSS file | | - index. The TSX TSX file | -- App. TSX | -- main. Ts entry documents | - router. Ts | - routing file. Editorconfig. | - prettierrc. | - postcssrc. Js | -- Babel. Config. Js | -- package. Json | - tsconfig.json |-- tslint.jsonCopy the code
Webpack configuration
- webpack.base.js
const path = require("path");
// Throw some configuration, such as port, builtPath
const config = require("./configs/options");
// CSS less SCSS loder integration
const cssLoaders = require("./rules/cssLoaders");
function resolve(name) {
return path.resolve(__dirname, "..", name);
}
// Development environment changes do not refresh the page, hot replacement
function addDevClient(options) {
if (options.mode === "development") {
Object.keys(options.entry).forEach(name= > {
options.entry[name] = [
"webpack-hot-middleware/client? reload=true&noInfo=true"
].concat(options.entry[name]);
});
}
return options.entry;
}
/ / webpack configuration
module.exports = options= > {
const entry = addDevClient({
entry: {
app: [resolve("src/main.ts")]},mode: options.mode
});
return {
// Webpack pack entry
entry: entry,
// Define options for how webpack outputs
output: {
publicPath: "/".// The output directory of the build file
path: resolve(config.builtPath || "dist"), // The destination path of all output files
filename: "static/js/[name].[hash].js".// Entry Chunk file naming template
chunkFilename: "static/js/[name].[chunkhash].js" // Name of a non-entry chunk file
},
resolve: {
// The module's lookup directory
modules: [resolve("node_modules"), resolve("src")].// Extension of the file used
extensions: [".tsx".".ts".".js".".vue".".json"].// List of module aliases
alias: {
vue$: "vue/dist/vue.esm.js"."@components": resolve("src/components"),
"@": resolve("src")}},// Prevent some import packages from being packaged into bundles,
// Get these external dependencies at runtime.
// Reduce the packing volume. Add CDN import on the home page
externals: {
vue: "Vue".vuex: "Vuex"."vue-router": "VueRouter"
},
// Module configuration
module: {
rules: [{test: /(\.jsx|\.js)$/.use: ["babel-loader"].exclude: /node_modules/
},
//.tsx file parsing
{
test: /(\.tsx)$/.exclude: /node_modules/.use: ["babel-loader"."vue-jsx-hot-loader"."ts-loader"] {},test: /(\.ts)$/.exclude: /node_modules/.use: ["babel-loader"."ts-loader"]},... cssLoaders({mode: options.mode,
sourceMap: options.sourceMap,
extract: options.mode === "production"{}),test: /\.(png|jpe? g|gif|svg)(\? . *)? $/.loader: "url-loader".options: {
limit: 10000.name: "static/img/[name].[hash:7].[ext]"}}, {test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/.loader: "url-loader".options: {
limit: 10000.name: "static/media/[name].[hash:7].[ext]".fallback: "file-loader"}}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/.loader: "url-loader".options: {
limit: 10000.name: "static/fonts/[name].[hash:7].[ext]"}}]}}; };Copy the code
Development Environment Configuration
- development.js
const webpack = require('webpack')
const path = require('path')
const express = require('express')
const merge = require('webpack-merge')
const chalk = require('chalk')
// Implement local service hot replacement
/ / https://github.com/webpack-contrib/webpack-hot-middleware
const webpackMiddleware = require('webpack-dev-middleware')
const webpackHotMiddleware = require('webpack-hot-middleware')
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
const baseConfig = require('./webpack.base')
const config = require('./configs/options')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const proxyTable = require('./configs/proxy')
// http-proxy-middleware adds a proxy
const useExpressProxy = require('./plugins/useExpressProxy')
// Global variables
const appEnvs = require('./configs/appEnvs')
const app = express()
// Merge webpack requests
const compiler = webpack(merge(baseConfig({ mode: 'development'{}),mode: 'development'.devtool: '#cheap-module-eval-source-map'./ / the plugin
plugins: [
new ProgressBarPlugin(), // Progress bar plug-in
new FriendlyErrorsWebpackPlugin(),
// Set a shortcut to the process.env environment variable via DefinePlugin.
new webpack.EnvironmentPlugin(appEnvs),
// Module hotreplace plugin, used with Webpack-hot-middleware
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: resolve('./public/index.html'),
filename: 'index.html'})].optimization: {
// Skip the build phase without exiting because of an error code
noEmitOnErrors: true}}))function resolve (name) {
return path.resolve(__dirname, '.. ', name)
}
const devMiddleware = webpackMiddleware(compiler, {
/ / with webpack publicPath
publicPath: '/'.logLevel: 'silent'
})
const hotMiddleware = webpackHotMiddleware(compiler, {
log: false
})
compiler.hooks.compilation.tap('html-webpack-plugin-after-emit', () => {
hotMiddleware.publish({
action: 'reload'})})// Load middleware
app.use(devMiddleware)
app.use(hotMiddleware)
// Add the proxy configuration
useExpressProxy(app, proxyTable)
devMiddleware.waitUntilValid((a)= > {
console.log(chalk.yellow(`I am ready. open http://localhost:${ config.port || 3000 } to see me.`))
})
app.listen(config.port || 3000)
Copy the code
Production Environment Configuration
- production.js
const webpack = require('webpack')
const path = require('path')
const ora = require('ora')
const chalk = require('chalk')
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
// Replace extract-text-webpack-plugin for extracting CSS files
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// For optimized compression of CSS files
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// Clear the dist file when rebuilding
const CleanWebpackPlugin = require('clean-webpack-plugin')
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
const config = require('./configs/options')
const appEnvs = require('./configs/appEnvs')
const compiler = webpack(merge(baseConfig({ mode: 'production'{}),mode: 'production'.output: {
publicPath: '/'
},
performance: {
hints: false
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new webpack.EnvironmentPlugin(appEnvs),
new webpack.SourceMapDevToolPlugin({
test: /\.js$/.filename: 'sourcemap/[name].[chunkhash].map'.append: false
}),
new CleanWebpackPlugin([`${config.builtPath || 'dist'}/ * `] and {root: path.resolve(__dirname, '.. ')}),new HtmlWebpackPlugin({
template: resolve('./public/index.html'),
filename: 'index.html'.chunks: ['app'.'vendors'.'mainifest'].minify: {
removeComments: true.collapseWhitespace: true.removeAttributeQuotes: true}}),new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash].css'
chunkFilename: 'static/css/[id].[contenthash].css'})].optimization: {
// Package the WebPack runtime generation code into mainifest
runtimeChunk: {
name: 'mainifest'
},
// Replace commonChunkPlugin to split code
splitChunks: {
chunks: 'async'.minSize: 30000.minChunks: 1.maxAsyncRequests: 5.maxInitialRequests: 3.automaticNameDelimiter: '~'.name: true.cacheGroups: {
// node_modules are merged into vendor.js
vendor: {
test: /node_modules\/(.*)\.js/.name: 'vendors'.chunks: 'initial'.priority: - 10.reuseExistingChunk: false
},
// Combine CSS into a single file with the mini-CSs-extract-plugin
styles: {
name: 'styles'.test: /(\.less|\.scss|\.css)$/.chunks: 'all'.enforce: true,}}},minimizer: [
ParallelUglifyPlugin enables serial compression of JS files to enable parallel execution of multiple sub-processes
new ParallelUglifyPlugin({
uglifyJS: {
output: {
beautify: false.comments: false
},
compress: {
warnings: false.drop_console: true.collapse_vars: true.reduce_vars: true}},cache: true.// Enable caching
parallel: true.// Parallel compression
sourceMap: true // set to true if you want JS source maps
}),
/ / compress CSS
new OptimizeCssAssetsPlugin({
assetNameRegExp: /(\.less|\.scss|\.css)$/g.cssProcessor: require("cssnano"), // CSS compression optimizer
cssProcessorOptions: {
safe: true.autoprefixer: { disable: true },
discardComments: { removeAll: true}},// Remove all comments
canPrint: true]}}})))function resolve (name) {
return path.resolve(__dirname, '.. ', name)
}
const spinner = ora('building for production... ').start()
compiler.run((err, stats) = > {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true.modules: false.children: false.chunks: false.chunkModules: false
}) + '\n\n')
console.log(chalk.cyan(' Build complete..\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'))})Copy the code
Vue + TypeScript
To use typescript in VUE, we use vue-class-Component. First we need to make the project JSX and typescript compatible
Babel 7
I used babel@7 with some changes compared to 6. All packages are @babel/ XXX
For JSX compatibility, I just used @vue/babel-preset- JSX and loaded babel-plugin-transform-jsx internally
- babel.config.js
module.exports = {
presets: [['@babel/preset-env',
{
modules: false.targets: {
browsers: ['> 1%'.'last 2 versions'.'not ie <= 8']}}],'@vue/babel-preset-jsx'].plugins: [
'@babel/plugin-transform-runtime'].comments: false.env: {
test: {
presets: ['@babel/preset-env'].plugins: ['babel-plugin-dynamic-import-node']}}}Copy the code
TsConfig
- tsconfig.js
{
"include": [
"src/**/*.ts"."src/**/*.tsx"."src/**/*.vue"."tests/**/*.ts"."tests/**/*.tsx"]."exclude": ["node_modules"]."compilerOptions": {
// typeRoots option has been previously configured
"typeRoots": [
// add path to @types
"src/@types"]."baseUrl": "."."paths": {
"*": ["types/*"]."@ / *": ["src/*"]},// Parse in strict mode
"strict": true.// Support JSX in.tsx files
"jsx": "preserve".// Use the JSX factory function
"jsxFactory": "h".// Allow default imports from modules that do not have default exports set
"allowSyntheticDefaultImports": true.// Enable the decorator
"experimentalDecorators": true.// "strictFunctionTypes": false,
// Allows javascript files to be compiled
"allowJs": true.// The module system used
"module": "esnext".// Compile the output target ES version
"target": "es5".// How to handle modules
"moduleResolution": "node".// There is an error with an implied any type on expressions and declarations
"noImplicitAny": true."importHelpers": true."lib": ["dom"."es5"."es6"."es7"."es2015.promise"]."sourceMap": true."pretty": true."esModuleInterop": true}}Copy the code
- tslint.js
{
"defaultSeverity": "warning"."extends": ["tslint:recommended"]."linterOptions": {
"exclude": ["node_modules/**"]},"allowJs": true."rules": {
"arrow-parens": false."trailing-comma": false."quotemark": [true]."indent": [true."spaces".2]."interface-name": false."ordered-imports": false."object-literal-sort-keys": false."no-console": false."no-debugger": false."no-unused-expression": [true."allow-fast-null-checks"]."no-unused-variable": false."triple-equals": true."no-parameter-reassignment": true."no-conditional-assignment": true."no-construct": true."no-duplicate-super": true."no-duplicate-switch-case": true."no-object-literal-type-assertion": true."no-return-await": true."no-sparse-arrays": true."no-string-throw": true."no-switch-case-fall-through": true."prefer-object-spread": true."radix": true."cyclomatic-complexity": [true.20]."member-access": false."deprecation": false."use-isnan": true."no-duplicate-imports": true."no-mergeable-namespace": true."encoding": true."import-spacing": true."interface-over-type-literal": true."new-parens": true."no-angle-bracket-type-assertion": true."no-consecutive-blank-lines": [true.3]}}Copy the code
Project code
With everything in place, we started writing.tsx files
- App.tsx
import { Vue, Component } from "vue-property-decorator";
import { CreateElement } from "vue";
@Component
export default class extends Vue {
// CreateElement (h: CreateElement)
// There is no automatic h injection, there is a problem with babel-plugin-transform-jsx
// Why is there no auto-injection
render(h: CreateElement) {
return (
<div id="app">
<router-view />
</div>); }}Copy the code
At the end
The article is not very detailed, in fact, many important project configurations have not been added, Examples include Commitizen, Lint-stage, Jest, Cypress, babel-plugin-vue-jsx-sync, babel-plugin-jsx-v-model…..
It is also about understanding the whole project construction process from scratch.
Code uploaded. Welcome. The project address