从零搭建一个属于本身的react框架

前言

事例一:咱们在使用react的时候,会用create-react-app命令下载一个react的模板,而后开始在里面实现各类功能。css

事例二:前几个月的时候,我偶然发现飞冰官网,浏览后以为不错,并在其上面下载了几个模板。html

那咱们可能好奇两件事情:前端

  • 这个模板是如何从一个空目录下搭建起来的呢
  • 这个模板搭建起来以后,是如何定义命令行下载的呢

本文将按照我本身的一个想法、以react模板样式为基准,就这两个话题展开叙述。vue

1、开始搭建

1.1 目录

├─ build                     // webpack配置目录
│  ├─ webpack.base.js        // webpack共有配置
│  ├─ webpack.dev.js         // webpack开发环境配置
│  └─ webpack.prod.js        // webpack生产环境配置
├─ public                    // 模板存放目录
│  ├─ favicon.ico            // 网站图标
│  └─ index.html             // 模板html文件
├─ src                       // 项目
│  ├─ common                 // 共有方法
│  ├─ compoents              // 自封装组件
│  ├─ layouts                // 布局组件 
│  ├─ pages                  // 页面
│  ├─ index.js               // 项目入口
│  ├─ App.css                // css样式
│  ├─ logo.svg               // 首页logo
│  └─ App.jsx                
├─ .babelrc                  // babel配置文件
├─ .browerslistrc            // 配置浏览器的兼容性范围
├─ .gitignore                // 忽略上传文件
├─ package.json              
├─ README.md                 // 工程搭建文档说明
复制代码

1.2 初始化

首先,咱们要建立一个空目录,而后初始化项目 npm init -y 执行完命令,咱们会在目录中看见一个package.json文件。 node

1.3 配置package.json

咱们知道,不管是项目的启动仍是打包,它的命令是从package.json文件中本身定义的。在建立的模板中,有start命令-启动react,有build-将react项目打包,目前咱们只配置这两个命令以及开发环境下的打包、生产环境下启动服务命令。react

找到pakage.json文件中的scripts,像这样配置webpack

"scripts": {
    "start": "webpack-dev-server --env.development --config ./build/webpack.base.js",      // 开发环境启动服务,
    "dev":"webpack --env.development --config ./build/webpack.base.js",                    // 开发环境打包代码
    "build": "webpack --env.production --config ./build/webpack.base.js",                  // 生产环境打包代码
    "build:server": "webpack-dev-server --env.production --config ./build/webpack.base.js" // 生产环境启动服务
  },
复制代码

这样,咱们完成了基础的配置,可是,咱们会思考:不一样的平台(MacWindows)是否是会出现设置环境变量不同的问题?基于这个问题,咱们找到了cross-env插件,那咱们应该如何将上面配置好的scripts改进呢?git

"scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --config ./build/webpack.base.js",
    "dev":"cross-env NODE_ENV=development webpack --config ./build/webpack.base.js",
    "build": "cross-env NODE_ENV=production webpack --config ./build/webpack.base.js",
    "build:server": "cross-env NODE_ENV=production webpack-dev-server --config ./build/webpack.base.js"
  },
复制代码

配置好之后,下一步,咱们就开始配置webpackgithub

1.4 配置webpack

1.4.1 webpack.base.js

const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const dev = require('./webpack.dev');
const prod = require('./webpack.prod');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const isDev = process.env.NODE_ENV === 'development';

const base = {
    entry: path.resolve(__dirname, '../src/index.js'),      // 入口
    module: {                                               
        rules: [{                                           // 对.js、.jsx的处理
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            use: 'babel-loader'
        }, {                                                // 对.css的处理
            test: /\.css$/,
            use: [
                !isDev && MiniCssExtractPlugin.loader,      // 生产环境下样式抽离
                isDev && 'style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        importLoaders: 1                    // 引入的文件调用后面的loader处理
                    }
                },
                {                                           // 智能添加样式前缀
                    loader: "postcss-loader",
                    options:{
                        plugins:[require('autoprefixer')]
                    }
                },
            ].filter(Boolean)
        }, {
            test: /\.scss$/,                               // css预处理器scss的处理
            use: [
                !isDev && MiniCssExtractPlugin.loader,     // 生产环境下抽离样式
                isDev && 'style-loader',
                "css-loader",
                "sass-loader"
            ].filter(Boolean)
        }, {
            test: /\.less$/,                               // css预处理器less的处理
            use: "less-loader",
        }, {
            test: /\.stylus$/,                             // css预处理器stylus的处理                           
            use: "stylus-loader",
        }, {
            test: /\.(jpe?g|png|svg|gif)$/,                // 对图片的处理
            loader: "file-loader",
            options: {
                name: "image/[contentHash].[ext]"
            },
        }, {
            test: /\.(woff|ttf|eot|otf|ico)$/,             // 对字体图标的处理
            loader: "file-loader",
            options: {
                name: "image/[name].[ext]"
            },
        }]
    },
    output: {                                              // 出口
        filename: 'scripts/[name].bundle.js',
        path: path.resolve(__dirname, '../dist')
    },
    resolve: {                                             // 引入js、jsx文件时,无需添加后缀
        extensions: ['.js', '.jsx'],
    },
    plugins: [
        !isDev && new MiniCssExtractPlugin({               // css样式抽离
            filename: 'css/[name].[contentHash].css'
        }),
        new HtmlWebpackPlugin({                            // 配置入口html
            filename: 'index.html',
            template: path.resolve(__dirname, '../public/index.html'),
            hash: true,
            inject: true,
            favicon: path.resolve(__dirname, '../public/favicon.ico'), 
            minify: !isDev && {
                removeAttributeQuotes: true,               // 去掉属性双引号
                collapseWhitespace: true,                  // 将html文件折叠成一行
            }
        }),
        new webpack.HotModuleReplacementPlugin(),
    ].filter(Boolean),
    devServer: {                                           // 配置服务
        hot:true,                                          // 热更新
        port: 3000,                                        // 端口号
        compress: true,                                    // 提高页面返回速度
        open: true,                                        // 启动服务后自动启动浏览器
        contentBase: path.resolve(__dirname, '../dist'),   // webpack启动服务会在dist目录下
    }
}

module.exports = () => {                                    // 根据环境合并webpack
    if (isDev) {
        return merge(base, dev);
    } else {
        return merge(base, prod);
    }
}
复制代码

1.4.2 webpack.dev.js

module.exports = {
    mode: 'development',
}
复制代码

1.4.3 webpack.prod.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
    mode: 'production',
    plugins: [
        new CleanWebpackPlugin(),                         // 打包前清空dist目录
        new UglifyJsPlugin({                              // 打包后自动去除debugger、console等        
            uglifyOptions: {
                compress: {
                    drop_debugger: true,               
                    drop_console: true,
                    pure_funcs: ['console.log', 'debugger']
                }
            },
            parallel: true
        }),
    ],
}
复制代码

1.5 查看效果

如今,咱们已经实现了一个属于本身的模板,可是咱们发现打包的速度可能会有一些慢。那究竟如何让本身的代码打包更快呢?

咱们能够优化咱们的webpack配置~web

关于webpack的配置优化,后续,会单独写一个文章~

2、自定义命令下载本身的模板

咱们都用过vue-clicreate-react-app等命令下载一个初始的vuereact模板,这里我要写一个属于本身的cli下载属于本身的模板(手动狗头)~

个人clinpm上的名字叫react-demo-cli,下载模板的命令是create-react-cli download

因此,在使用的时候,先输入命令 npm install react-demo-cli -g 而后 create-react-cli download 就能够啦~

效果是这样婶滴

最后

这一篇文章的核心简单来讲有两条

  • 配置webpack
  • 搭建一个本身的cli 只要这两件事情搞定了,问题就不难了

这里,主要说了从零搭建本身的react框架的思路、方法,效果和create-react-app等比起来,仍是差不少。因此,我会不断优化本身和本身的代码~

还有:

  • 对于cli,只有使用方法,后续会有专门的文章讲解如何搭建一个本身的cli。这里还要感谢圈圈的圈的cli代码讲解~
  • 对于webpack,我使用了webpack-dev-server来启动服务,这样启动的服务在控制台打印了不少东西,因此后续会改成本身编写脚本的方式~

下面,是clireact-templategithub地址

上面的文章若有不对之处,还请你们指点出来~咱们共同进步~

而后,分享一下个人公众号「web前端日记」的二维码,欢迎前来你们关注~

相关文章
相关标签/搜索