Web 工程化基础

吴心役
2023-12-04

NPM

NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:

  • 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
  • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

命令

  1. 更新npm:npm install npm -g,一般情况下node.js已经集成了npm只需要安装node.js就可以了。
  2. 查看npm版本: npm -v
  3. 本地安装:本地安装:npm install <Module Name>,程序包被安装在node_moudles目录下,使用时直接导入即可(inport name from 'Module Name'),不需要指定路径。
  4. 全局安装程序包:npm install <Module Name> -g
  5. 所有查看全局程序包:npm list -g
  6. 查看本地程序包:npm ls
  7. 查看某个程序包信息:npm view <Moudle Name>
  8. 安装指定版本包:npm install <Moudle Name>@<版本号>
  9. 卸载包:npm uninstall <Moudle Name>
  10. 更新程序包:npm update <Moudle Name>
  11. 搜索程序包:npm search <Moudle Name>
  12. 创建package.json文件: npm init

配置文件

package.json:包属性属性说明文件

  • scripts:实际运行命令,可以给包命令重新命名别名。

  • name:包名

    • version:包的版本号
    • description:包的描述
    • homepage:包的官网url
    • author:包的作者姓名
    • contributors:包的其它贡献者姓名
    • dependencies:包的依赖包列表,如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。
    • repository:包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。
    • main:main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。
    • keywors:关键字

package-lock:包版本锁定文件,它是package.json文件的映射。

Webpack

基本使用

  1. 初始化,会创建一个 package.json 文件(包属性说明文件)

    npm init -y

  2. 安装 webpack-cli ,这个工具用于在命令行中运行 webpack

    npm install webpack webpack-cli --save-dev

  3. 修改 package.json 文件

    +  "private": true, # 将打包器改为私有
    -  "main": "index.js", # 去掉入口文件,放置打包意外发布
    
  4. 创建源代码目录,一般情况下源码放在 /src 目录中。

  5. 创建分发代码目录(即打包发布代码),一般情况下分发代码放在 /dist 目录中。

  6. 创建 webpack 配置文件,webpack.config.js 文件。

  7. /dist 中创建 index.html 文件,在/src 中创建 index.js 文件,index.html 作为项目的入口文件,index.js 作为项目入口文件 index.html 的依赖文件,此时项目目录为:

    webpack-demo
      |- package.json
      |- webpack.config.js
      |- /dist
         |- index.html
      |- /src
         |- index.js
    

    webpack.config.js

    const path = require("path")
    
    module.exports = {
    	entry: './src/index.js',
    	output: { //__dirname 是设置文件的存放目录
    		filename: 'main.js', //输出文件重命名
    		path: path.resolve(__dirname, 'dist') //输出文件存放地址
    	}
    }
    

    index.html

    <html>
    <head>
    </head>
    <body>
        <p>webpack示例</p>
    	<script src="main.js"></script>
    </body>
    </html>
    

    index.js

    window.onload = function(){
    	console.log("hello")
    }
    
  8. 使用配置文件打包,npx webpack --config webpack.config.js

    打包成功后会在分发代码目录输出入口文件(mian.js)。

概念

  1. entry 入口:设置打包器的起点(主页引用的js文件)。配置:

    module.exports = {
        entry: './entry.js'  //设置entery.js文件为入口起点
    }
    
  2. output 出口:设置打包的输出路径,以及如何命名打包文件,默认输出路径为./dist。配置:

    const path = require('path') //引入node.js中的path模块
    moudle.exports = {
        output: {
            path: path.resolve(_dirname, 'dist'), 
            //设置输出路径,path.resolve()是node.js的路径操作方法。
            filename: 'filename.bundle.js'
            //设置打包文件名
        }
    }
    
  3. loader 装载:webpack只能处理JavaScript文件,loader的作用是将非JavaScript文件转换为webpack能够处理的文件。配置:

    const path = require('path')
    const config = {
        moudle: {
            rules: [
                {test: /\.txt$/, use: 'raw-loader'}
                //当碰到.txt路径的文件时,使用raw-loader转换一下再打包。
            ]
        }
    }
    module.exports = config
    
  4. plugins 插件:loader用于转换某些类型的模块,plugins用于解决loader解决不了的事情。配置:

    const HtmlWebpackPlugin = require('html-webpack-plugin') //通过npm安装
    const webpack = require('webpack')  //访问内置插件
    const config = {
        plugins: [
            new HtmlWebpackPlugin({template: './index.html'})
        ]
    }
    module.exports = config
    
  5. mode 模式:通过选择developmentproduction之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化。配置:

    module.exports = {
      mode: 'production'
    }
    
  6. vue.config.js 是vue-cli的webpack的配置,没有的话在项目跟目录下创建一个即可。

管理资源

通过插件让 webpack 能够处理非 JavaScript 的方式完成对资源的管理使用。

基本使用

  • 加载 CSS ,安装依赖的软件包,npm install --save-dev style-loader css-loaderCSS 文件是用过 import 'url' 引入相应的 .js 文件中的。

  • 加载图片,安装依赖的软件包,npm install --save-dev file-loader ,图片是在

  • 加载字体,file-loaderurl-loader 可以接收并加载任何文件,然后将其输出到构建目录,加载字体文件直接加载即可。

  • 加载数据(CSVTSVXML),安装依赖的软件包,npm install --save-dev csv-loader xml-loader

  • 示例:

    webpack.config.js

    const path = require('path')
    
    module.export = {
        enter: {},
        output: {},
        module: {
            rules: [
                { //加载css文件
                    test: /\.css$/, //根据正则表达式的语法,匹配所有以 .css 结尾的文件
                    use: [ //文件的处理模块
                        'style-loader',
                        'css-loader'
                    ]
                },
                { //加载图片
                    test: /\.(png|svg|jpg|gif)$/,
                    use: [
                        'file-loader'
                    ]
                },
                { //加载字体
                    test: /\.(woff|woff2|eot|ttf|oft)$/,
                    use: [
                        'file-loader'
                    ]
                },
                { //加载 csv、tsv 数据
                    test: /\.(csv|tsv)$/,
                    use: [
                        'csv-loader'
                    ]
                },
                { //加载 xml 数据
                    test: /\.xml$/,
                    use: [
                        'xml-loader'
                    ]
                }
            ]
        }
    }
    

    此时项目目录:

    webpack-demo
      |- package.json
      |- webpack.config.js
      |- /dist
         |- index.html
      |- /src
         |- index.js
         |- index.css
         |- ex.tff
         |- ex.jpg
    

    index.css

    @font-face { /*引入字体文件*/
    	font-family: "ex";
    	src: url("./ex.ttf");
    }
    
    body {
    	font-family: "ex"; /*设置使用字体*/
    	color: blue;
    	background: url("./ex.jpg"); /*设置背景图片*/
    }
    

输出管理

管理输出是指使用 webpack 自动的将有关联的文件关联起来,如在 index.html 文件中引入 index.js 文件一般需要在 index.html 中使用 <script> 标签,而管理输出的目的是让有关联的文件自动的关联起来,不用在文件中显示引入。

基本使用

  • 管理输出,引入依赖包: npm install --save-dev html-webpack-plugin

  • 清理 /dist 文件夹,引入依赖包: npm install clean-webpack-plugin --save-devwebpack 不会自动清理输出文件夹,避免文件夹中积累上次的打包文件,可以设置清理输出文件夹然后再打包。

  • 示例:

    webpack.config.js

    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const {CleanWebpackPlugin} = require('clean-webpack-plugin')
    
    module.exports = {
        entry: { //入口文件依赖
            index: './src/index.js',
            one: './src/one.js'
        },
        plugins: [
            new CleanWebpackPlugin(), //默认清除输出文件目录
            new HtmlWebpackPlugin({ //管理输出
                template: path.resolve(__dirname, './src/index.html'), //入口文件
                chunks: ["index", "one"] //入口文件依赖
            })
        ],
        output: {
            filename: '[name].bundle.js',
            path: path.resolve(__dirname, 'dist')
        }
    }
    

    此时文件目录:

    webpack-demo
      |- package.json
      |- webpack.config.js
      |- /dist
      |- /src
         |- index.html
         |- index.js
         |- one.js
         |- index.css
         |- ex.tff
         |- ex.jpg
    

开发环境

webpack 打包源代码时,会有很难追踪到错误和警告在源代码中的原始位置等问题,开发的目的就是使错误追踪等能够映射到源代码文件,使代码热更新,浏览器自动刷新等问题。

基本使用

  • 追踪错误源代码,在 webpack.config.js 文件中使用 inline-source-map 选项。

  • 使用 npx webpack --watch 命令启动观察者模式打包,此时打包完成命令不会退出,当代码有变化时,会实时响应。

  • 浏览器自动刷新,安装相应依赖包,npm install --save-dev webpack-dev-server ,并使用 npx webpack-dev-server --open 启动服务器,浏览器自动刷新会默认使用观察者模式。

    浏览器自动刷新,安装相应依赖包, npm install --save-dev express webpack-dev-middlewarewebpack-dev-middleware 是一个容器,可以把 webpack 处理后的文件传递给一个服务其, webpack-dev-server 是内置了 webpack-dev-middleware ,但单独使用 webpack-dev-middleware 能更灵活的配置自定义设置。(使用方式略过)

  • 示例:

    webpack.config.js

    const path = require("path")
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const {CleanWebpackPlugin} = require('clean-webpack-plugin')
    
    module.exports = {
        entry: {},
        devtool: "inline-source-map", //追踪错误源码
        devServer: { //浏览器自动刷新
          contentBase: './dist' //将 dist 目录下的文件,作为可访问文件。
        },
        plugins: [],
        output: {}
    }
    

生产环境

为开发环境和生产环境构建不一样的 webpack.config.js 配置文件。

基本使用

  • 安装依赖包:npm install --save-dev webpack-merge ,将 webpack.config.js 拆分成 webpack.common.js (通用)和 webpack.dev.js (开发环境) 、 webpack.prod.js (生产环境) 三个文件,其中通用的文件是开发环境和生产环境公用的配置。

  • 启用开发环境打包: npx webpack --config webpack.dev.js

  • 启用生产环境打包: npx webpack --config webpack.prod.js

  • 示例:

    webpack.common.js

    const path = require("path")
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const {CleanWebpackPlugin} = require('clean-webpack-plugin')
    
    module.exports = {
    	entry: {
            index: './src/index.js',
            one: './src/one.js'
        },
    	plugins: [
    	    new CleanWebpackPlugin(),
            new HtmlWebpackPlugin({
                template: path.resolve(__dirname, './src/index.html'),
                chunks: ["index", "one"]
            })
        ],
    	output: {
            filename: '[name].bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
    	module: {
            rules: [
                {
                    test: /\.css$/,
                    use: [
                        'style-loader',
                        'css-loader'
                    ]
                },
    			{
                    test: /\.(png|svg|jpg|gif)$/,
                    use: [
                        'file-loader'
                    ]
                },
    			{
                    test: /\.(woff|woff2|eot|ttf|oft)$/,
                    use: [
                        'file-loader'
                    ]
                }
            ]
        } 
    }
    

    webpack.dev.js 配置

    const {merge} = require('webpack-merge')
    const common = require('./webpack.common.js')
    
    module.exports = merge(common, {
        devtool: 'inline-source-map',
    })
    

    webpack.prod.js 配置

    const {merge} = require('webpack-merge')
    const common = require('./webpack.common.js')
    
    module.exports = merge(common, {
    })
    

    此时文件目录为:

    webpack-demo
      |- package.json
      |- webpack.common.js
      |- webpack.dev.js
      |- webpack.prod.js
      |- /dist
      |- /src
         |- index.html
         |- index.js
         |- one.js
         |- index.css
         |- ex.tff
         |- ex.jpg
    

代码分离

代码分离是将代码分离到更小的文件中,然后按需加载,代码分离可以获得更小的文件以及控制资源加载的优先级,使用得当的话,会极大提升加载时间。有三种常用的分离方法:

  1. 入口起点:使用 entry 配置手动分离代码,即手动的将入口模块才分开来。
  2. 防止重复:使用 CommonsChunkPlugin 去重和分离 chunk 。即将入口起点分为多个模块时,假如多个模块引用了同一个依赖,已导入依赖重复引入,防止重复就是以将重复的依赖单独提取到一个模块中去防止重复导入的方法。
  3. 动态导入:通过模块的内敛函数调用来分离代码。

入口起点:webpack.config.js 配置

module.exports = {
    entry: {//两个入口文件
        one: './src/one.js',
        two: './src/one.js'
    }
}

防止重复:webpack.config.js 配置

const webpack = require('webpack')

module.exports = {
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'common' //提取的公共模块的打包文件名
        })
    ]
}

配置示例

const path = require('path');  //引入node.js内置的处理路径的path模块
const HtmlWebpackPlugin = require('html-webpack-plugin');//用于生成入口文件的模块,需要安装(npm i -D html-webpack-plugin)
const {CleanWebpackPlugin} = require('clean-webpack-plugin');//用于清空上一次打包文件夹内的打包文件,需要安装(npm install clean-webpack-plugin)
//解析CSS,需要根据使用CSS文件类型安装响应程序包,.css文件安装(npm install --save-dev style-loader css-loader)
const MiniCssExtractPlugin = require("mini-css-extract-plugin");//用于将引入的css文件提取成单独的文件,一般情况下系统默认把css文件提取到js文件中。
[const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin'); 用于将单独的css文件打包成单独的css文件,"mini-css-extract-plugin"会把所有的css文件打包成一个css文件。使用实例暂略。]
//打包图片,打包字体文件,打包媒体文件,需要安装(npm install --save-dev file-loader)

module.exports = {
  mode: 'development', //开发模式打包的文件模式,production是生产模式
  entry: {//入口文件,就是index.html文件需要引入多少js文件,可以一个html对应一个或多个入口js文件。
      main: path.resolve(__dirname, 'url')//入口文件的路径
      one: path.resolve(__dirname, 'url')//__dirname是当前配置文件所在的路径
      two: path.resolve(__dirname, 'url')//url是入口文件相对于配置文件的路径
  },
  output: {//出口文件,就是打包后的文件的配置
    filename: '[name].[hash:8].js',//打包后的文件名,[name]表示保留原文件名,[hash:8]表示生成随机的字符
    path: path.resolve(__dirname, 'dist')  //打包文件的输出目录
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve('__dirname,'url/xxx.html''),//引用入口文件的HTML文件路径
      filename: 'index.html',//打包后的HTML的文件名
      chunks: ['main', 'one']//需要引入的入口js文件列表,这个html文件引入两个js文件
    }),
    new HtmlWebpackPlugin({
      template: path.resolve('__dirname,'url/xxx.html''),//引用入口文件的HTML文件路径
      filename: 'two.html',//打包后的HTML的文件名
      chunks: ['two']//需要引入的入口js文件列表,这个html文件引入一个js文件
    }),
    new CleanWebpackPlugin(),//清空上一次打包的打包文件
    new MiniCssExtractPlugin({
        filename: '[name].[hash:8].css',//打包后的css文件名
        chunkFilename: '[id].css'//识别需要引入的入口js文件中的css文件
    })
  ],
  moudle: {//用于对模块的源代码进行转换
    rules: [
      {//打包在入口JS文件中使用import引入的css样式表
        test: /\.css$/,//检查文件类型
        use: [//使用模块,注意模块的调用顺序,从数组右边往左边走
          MiniCssExtractPlugin.loader//将人口js引入的css文件提取到单独css文件中,不能和'style-loader'同时使用
          'style-loader',//把css文件引入js入口文件,css会放入入口js文件中,不能和MiniCssExtractPlugin.loader同时使用
          'css-loader',//打包css使用的模块,
        ]
      },
    ]
  }
};

node构建web示例

  1. 文件结构

    tmp
    index.js              => 应用主文件
    requestHandlers.js    => API处理
    router.js             => 路由处理
    server.js             => 服务器
    
  2. 构建服务器模块 server.js

    const http = require('http')   // 调用 http 模块,用于启动一个服务器
    const url = require('url')     // 调用 url 模块,用于处理链接
    
    function start(route, handle) {
        
      function onRequest(request, response) {
        const pathname = url.parse(request.url).pathname
        // 解析请求体中的url,获取pathname
        route(handle,pathname,response,request)
        // 调用函数 route
      }
        
      http.createServer(onRequest).listen(8888)
      // 创建一个监听 8888 端口的 http.Server 实例,并为 request 事件绑定 onRequest 回调函数
    }
    
    exports.start = start
    // 导出函数
    
  3. 构建路由处理主模块 router.js

    function route(handle, pathname,response,request){
      if(typeof handle[pathname] === 'function'){
        // 如果路由存在,则调用相应的处理函数
        handle[pathname](response,request)
      }else{
        // 如果路由不存在,则直接返回相应提示
        response.writeHead(404,{"Content-Type":"text/plain"});
        response.write("404 Not found");
        response.end();
      }
    }
    
    exports.route = route
    
  4. 构建路由处理函数模块 routerHandle.js

    这里,只封装五个路由的处理函数,分别是:

    • /start => 主页面路由
    • /uploadupload => 主页面中上传图片的 POST 请求
    • /viewview => 请求预览上传的图片的
    • /writewrite => 主页面中填写文本的 GET
    • /texttext => 预览填写的文本
    const querystring = require("querystring")  // 解析 postData 模块
    const fs = require("fs")                    // 读取文件模块
    const formidable = require("formidable")    // 文件上传模块, formidable 插件需要安装
    
    function start(response){  // 返回一个 html 页面
      console.log('one')
      const body ='<html>'+
        '<head>'+
        '<meta http-equiv="Content-Type" content="text/html; '+
        'charset=UTF-8" />'+
        '</head>'+
        '<body>'+
        '<form action="/upload" enctype="multipart/form-data" method="post">'+
        '<input type="file" name="upload">'+
        '<input type="submit" value="Submit text" />'+
        '</form>'+
        '</body>'+
        '</html>';
    
        response.writeHead(200,{"Content-Type":"text/html"});
        response.write(body);
        response.end();
    }
    
    function upload(response, request){ // 上传
      const form = new formidable.IncomingForm()
      form.uploadDir='tmp'
      form.parse(request,(error, fields, files)=>{ // 解析文件
        fs.renameSync(files.upload.path,"./tmp/test.png") // 将文件存储在指定位置
        response.writeHead(200,{"Content-Type":"text/html"})
        response.write("received image:<br/>") // 返回信息
        response.write("<img src='/show' style='width: 300px' />") // 返回信息
        response.end()
      })
    }
    
    function show(response, postData){ // 预览
      fs.readFile("./tmp/test.png", "binary", (error, file) => {
        if(error){
          response.writeHead(500, {"Content-Type":"text/plain"})
          response.write(error + "\n")
          response.end()
        }else{
          response.writeHead(200,{"Content-Type":"image/png"})
          response.write(file,"binary")
          response.end()
        }
      })
    }
    
    exports.start = start
    exports.upload = upload
    exports.show = show
    
  5. 构建应用主文件 index.js

    const server = require('./server')  // 导入 server.js 模块
    const router = require('./router')  // 导入 router.js 模块
    const routerHandle = require('./routerHandle')  // 导入 routerHandle.js 模块
    const handle = {}
    handle['/'] = routerHandle.start
    handle['/start'] = routerHandle.start
    handle['/upload'] = routerHandle.upload
    handle['/show'] = routerHandle.show
    server.start(router.route, handle)  // 开始服务器