Build the Preact development environment from scratch

motivation

React is a great library, but it’s a bit overkill on mobile. So I chose Preact when I was working on my personal project.

It’s worth noting that Preact implements a lot of react functionality, but preact also sacrifices some of react functionality. Preact is only 2KB.

different

Preact does a lot of development tricks on the API, such as passing props and state in the Render method. In order to simplify, Preact has dropped some features, mainly the following:

1. 'PropsType' validation (with a less useful function) 2. 'props. Children' is always an array (not too different) 3. 4. The 'render' method, 'preact' accepts a third argument (not much use)Copy the code

compromise

Preact-compat provides complete reactAPI and feature support.

The preparatory work

My file directory is as follows

SRC assets less(store less files) TSX (store TSX files) index.html index.tsx config webpack.config.js (development configuration) Webpack.build.config.js (production configuration).babelrc (Babel configuration file) tsconfig.json (typescript configuration file)Copy the code

Since you are starting from scratch, the first thing you need to determine is the technology stack. Based on my past experience, my stack of choice is less + typescript. Choose to use WebPack as the packaging tool. The following loaders naturally come to mind:

  1. Less – loader (needsless)
  2. css-loader
  3. Style-loader (for development environments)
  4. Ts – loader (needstypescript)
  5. Url-loader (font, image, etc.)
  6. Babel – loader (needsbabel-core)

Install the preceding loaders and corresponding required packages.

First install WebPack, I chose to use the 4.x version. Next install babel-core, webpack-cli, webpack-dev-server,babel-preset-env

  yarn add webpack webpack-cli webpack-dev-server babel-preset-env --dev
Copy the code

Next, we install code separation and plug-ins needed for development. Note that under Webpack, you need to install extract-text-webpack-plugin@next, otherwise you will get an error.

    yarn add clean-webpack-plugin html-webpack-plugin extract-text-webpack-plugin@next --dev
Copy the code

Webpack configuration

Next, let’s start configuring the webPack configuration file. I divide configuration files into two categories, one for production and one for development. And put them under {base_path}/config.

  // webpack.config.js
const html = require('html-webpack-plugin');
const clean = require('clean-webpack-plugin');
const path = require('path');
const extract = require('extract-text-webpack-plugin');

module.exports = {
    entry: './src/index.tsx'.module: {
        rules: [{test: /\.tsx? $/.use: ['babel-loader'.'ts-loader'] {},test: /\.less$/.use: [
                    'style-loader', 
                    {
                        loader: 'css-loader'.options: {
                            modules: true}},'less-loader'] {},test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/i.loader: 'url-loader'.options: {
                    limit: 1000.name: 'static/fonts/[name].[hash].[ext]'}}, {test: /\.(png|svg|jpg|gif)$/i.loader: 'url-loader'.options: {
                    name: 'static/img/[name].[hash].[ext]'.limit: 4096}}},mode: 'development'.resolve: {
        extensions: ['.jsx'.'.js'.'.tsx'.'.ts']},plugins: [
        new html({
            template: './src/index.html'
        }),
        new clean([path.resolve('./dist')] and {root: path.resolve('/')})],devtool: 'source-map'.devServer: {
        contentBase: path.resolve("./dist")}}Copy the code

To enable CSS modules, the modules parameter of csS-Loader must be set to true.

The production configuration files don’t differ much, but the CSS files have been pulled out and the code has been separated. [email protected] simplifies code separation by simply using splitChunks.

// webpack.build.config.js
const html = require('html-webpack-plugin');
const clean = require('clean-webpack-plugin');
const path = require('path');
const extract = require('extract-text-webpack-plugin');

module.exports = {
    entry: path.resolve('./src/index.tsx'),
    module: {
        rules: [{test: /\.tsx? $/.use: ['babel-loader'.'ts-loader'] {},test: /\.less$/.use: extract.extract({
                    use: [{loader: 'css-loader'.options: {
                                sourceMap: true}}, {loader: 'less-loader'.options: {
                                sourceMap: true}}]})}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/i.loader: 'url-loader'.options: {
                    limit: 1000.name: 'static/fonts/[name].[hash].[ext]'}}, {test: /\.(png|svg|jpg|gif)$/i.loader: 'url-loader'.options: {
                    name: 'static/img/[name].[hash].[ext]'.limit: 4096}}},mode: 'production'.resolve: {
        extensions: ['.jsx'.'.js'.'.tsx'.'.ts']},optimization: {
        splitChunks: {
            cacheGroups: {
                vender: {
                    name: 'vendor'.minSize: 0.chunks: 'initial'.test: /node_modules/,}}},runtimeChunk: {
            name: 'manifest'
        },
        minimize: true
    },
    output: {
        path: path.resolve('./dist'),
        filename: 'static/js/[name].[hash:8].js'.chunkFilename: 'static/js/[name].[chunkhash:8].js'.publicPath: '/'
    },
    plugins: [
        new html({
            template: './src/index.html'
        }),
        new clean([path.resolve('./dist')] and {root: path.resolve('/')}),new extract({
            filename: 'static/css/[name].[hash:8].css'.allChunks: true}})]Copy the code

It’s worth mentioning that I use path.resolve when dealing with road problems. In this case, the./ directory does not correspond to the directory where the config file resides, but to the project root directory.

Next we add two commands to package.json:

  "scripts": {
    "serve": "webpack-dev-server --config ./config/webpack.config.js",
    "build": "webpack --config ./config/webpack.build.config.js"
  }
Copy the code

Have you finished? Of course not, and the main thing is the configuration of Babel.

Babel configuration

In addition to configuring perset, Babel also needs to configure an important transform-React-jsx plug-in. This plugin is intended to convert JSX syntax. However, it defaults to converting the JSX syntax to the Virtual DOM wrapped by the react. createElement function. We need to make a change. Traditionally, we use preact.h instead.

// .babelrc
{
    "presets": [["env",
            {
                "modules": false}]],"plugins": [["transform-react-jsx",
            {
                "pragma": "preact.h"}}]]Copy the code

Typescript configuration

There is only one caveat here, and that is that the JSX syntax should be preserved and not converted. According to the above configuration file, the TS file is passed first to ts-loader and then to babel-loader. We hand over JSX syntax to Babel for conversion, so it needs to be preserved.

{
    "compilerOptions": {
        "target": "es2016"."moduleResolution": "node"."jsx": "preserve"."sourceMap": true}}Copy the code

conclusion

Webpack configuration is really a long story… In addition, the development process, also need to pay attention to the place. CSS modules do not use import, use require. Of course, you have to define a require yourself. In addition, JSX syntax files must be imported into preact packages, and there are some considerations.

import * as preact from 'preact'; // Must import as * as preact

declare function require(. args: any[]) :any; / / definerequirefunctionconst test = require('.. /less/test.less'); 

export default class App extends preact.Component<any.any> {
    constructor(props) {
        super(props);
    }
    
    render(props, state) {
        return (
            <div className={ test.test} >hello world!</div>)}}Copy the code

That’s basically it, webPack configuration is still a hassle. It’s not as magical as the legendary 0 configuration. When it comes to customizing features, you still need to be familiar with WebPack.

Configuration finished another afternoon no…