主頁 > 前端設計 > webpack學習筆記

webpack學習筆記

2021-09-26 13:32:45 前端設計

首先貼上自己關鍵插件版本

    "webpack": "^5.52.0",
    "webpack-cli": "^4.8.0",
    "webpack-dev-server": "^4.1.1"

本文參考依賴較多,請注意配置是否已失效,

一、初識webpack

1、組態檔名稱

webpack默認組態檔:webpack.config.js

可以通過webpack --config指定組態檔

module.exports = {
    entry: './src/index.js', // 4.0會默認制定入口位置為‘src/index.js’
    output: './dist/main.js', // 4.0會默認制定入口位置為‘dist/main.js’
    mode: 'production', // 環境
    module: {
        rules: [ // loader配置
            { 
                test:/\.txt$/, use: 'raw-loader'
            }
        ]
    },
    plugins:[
        new HtmlwebpackPlugin({ // 插件配置
            template: './src/index.html'
        })
    ]
}

2、安裝nvm

安裝 nvm(node.js version management,顧名思義是一個nodejs的版本管理工具,通過它可以安裝和切換不同版本的nodejs,下面列出下載、安裝及使用方法,)

安裝命令:

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash

or Wget:

wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash

安裝完之后添加到環境變數,

source ~/.bash_profile

// 推出并重啟終端,查看是否安裝成功: 
nvm --version

// 安裝node.js: 
nvm i v10.15.3

// 創建專案檔案夾,并初始化
mkdir 01project
cd 01project
// 所有詢問都是yes
npm init -y
//安裝webpack
npm i  webpack webpack-cli --save-dev
//查看專案是否安裝成功
./node_modules/.bin/webpack -v
webpack 5.52.0
webpack-cli 4.8.0

3、一個簡單例子

新建webpack.config.js檔案

const path = require('path')

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    mode: 'production'
}

新建src/index.js、src/helloworld.js檔案

// index.js檔案
import { helloworld } from "./helloworld";
document.write(helloworld())
// helloworld.js檔案
export function helloworld(){
    return 'hello webpack'
}

運行./node_modules/.bin/webpack命令,打包檔案

新建dist/index.html檔案,并引入打包檔案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="./bundle.js"></script>
</body>
</html>

4、通過npm script運行webpack

為什么package.json可以直接運行node_module/.bin的命令

原理:模塊區域安裝會在node_module/.bin目錄創建軟鏈接

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build":"webpack"
  },

新建src/serach.js檔案

document.write('search info')

修改webpack.config.js檔案 //通過占位符確保檔案名稱唯一

const path = require('path')

module.exports = {
    entry: {
        'index': './src/index.js',
        'search': './src/search.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js'
    },
    mode: 'production'
}

修改dist/index.html檔案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="./index.js"></script>
    <script src="./search.js"></script>
</body>
</html>

5、核心概念之Loaders

webpack開箱即用只支持JS和JSON兩種檔案型別,通過Loaders去支持其他檔案型別并且把她們轉化成有效的模塊,并且可以添加到依賴圖中,

本身是一個函式,接受源檔案作為引數,回傳轉換的結果,

常用的loaders有哪些

6、核心概念之Loaders

插件用于bundle檔案的優化,資源管理和環境變數注入,作用域整個構建程序,

二、常用Loaders

2.1、決議es6、React JSX

2.1.1 使用babel-loader

安裝相關依賴

npm install -D babel-loader @babel/core @babel/preset-env webpack

webpack配置

module:{
        rules: [
            { 
                test: /\.js$/,
                use: 'babel-loader'
            }
        ]
    }

為了支持es6需要增加配置

    module:{
        rules: [
            { 
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    option: {
                        presets: ["@babel/preset-env"],
                        plugins: ['@babel/plugin-proposal-object-rest-spread']
                    }
                }
            }
        ]
    }

又或者給babel一個組態檔.babelrc,新建.babelrc檔案

{
    "presets": ["@babel/preset-env"]
}

2.1.2 決議React JSX

安裝相關依賴

npm install -D react-dom @babel/preset-react 

修改.babelrc檔案

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}

修改src/search.js檔案,撰寫react組件

import React from 'react'
import ReactDOM from 'react-dom'
class Search extends React.Component{
    render() {
        return <div>search components</div>
    }
}

ReactDOM.render(
    <Search/>,
    document.getElementById('root')
)

2.2 css決議相關

2.2.1css-loader、style-loader(css-loader決議css、style-loader將樣式通過<style>標簽插入到head中)

安裝依賴

npm i -D css-loader style-loader

修改webpack.config.js檔案

 module:{
        rules: [
            { 
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                }
            },
            { 
                test: /\.css$/,
                use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
                    'style-loader',
                    'css-loader',
                ]
            }
        ]
    }

新建src/css/index.css檔案

.search-txt {
    font-size: 20px;
    color: blue;
}

在src/search.js檔案中參考css檔案

import React from 'react'
import ReactDOM from 'react-dom'
import './css/index.css'
class Search extends React.Component{
    render() {
        return <div className="search-txt ">search components</div>
    }
}

ReactDOM.render(
    <Search/>,
    document.getElementById('root')
)

2.2.2 決議less和sass

安裝依賴

npm i -D less less-loader

修改src/css/index.css檔案為src/css/index.less

修改src/css/search.js內關于index.css的參考名

修改webpack檔案

module:{
        rules: [
            { 
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                }
            },
            { 
                test: /\.css$/,
                use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
                    'style-loader',
                    'css-loader',
                ]
            },
            { 
                test: /\.less$/,
                use: [ 
                    'style-loader',
                    'css-loader',
                    'less-loader',
                ]
            }
        ]
    }

2.3 資源決議

2.3.1 file-loader用于處理圖片

安裝依賴npm i -D file-loader

在src/img檔案夾下放置一張圖片

在src/search.js檔案中引入圖片

import React from 'react'
import ReactDOM from 'react-dom'
import './css/index.less'
import fileImg from './img/fileImg.png'
class Search extends React.Component{
    render() {
        return <div className="search-txt ">search components
            <img src={fileImg}></img>
        </div>
    }
}

ReactDOM.render(
    <Search/>,
    document.getElementById('root')
)

添加相關配置

module:{
        rules: [
            { 
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                }
            },
            { 
                test: /\.css$/,
                use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
                    'style-loader',
                    'css-loader',
                ]
            },
            { 
                test: /\.less$/,
                use: [ 
                    'style-loader',
                    'css-loader',
                    'less-loader',
                ]
            },
            { 
                test: /\.(png|jpeg|gif|jpg)$/,
                use: [ 
                    'file-loader']
            }
        ]
    }

2.3.1 file-loader用于處理字體

新建src/font檔案夾,放入字體檔案

css引入,修改src/css/index.css檔案

@font-face{
    font-family: 'liuKai';
    src: url('../font/liuKai.ttf')
}
.search-txt {
    font-size: 20px;
    color: blue;
    font-family: 'liuKai';
}

修改webpack配置

module:{
        rules: [
            { 
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                }
            },
            { 
                test: /\.css$/,
                use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
                    'style-loader',
                    'css-loader',
                ]
            },
            { 
                test: /\.less$/,
                use: [ 
                    'style-loader',
                    'css-loader',
                    'less-loader',
                ]
            },
            { 
                test: /\.(png|jpeg|gif|jpg)$/,
                use: [ 
                    'file-loader']
            },
            { 
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [ 
                    'file-loader']
            }
        ]
    }

2.3.2 url-loader也可以處理圖片和字體,可以設定較小資源自動base64

安裝依賴

npm i -D url-loader

webpack.config.js中修改關于圖片處理的部分

            { 
                test: /\.(png|jpeg|gif|jpg)$/,
                use: [ 
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 10240 // 限制大小為10k
                        }
                    }]
            },

2.4 檔案監聽(watch)

檔案監聽是在發現原始碼發生變化時,自動重新構建出新的輸出檔案

webpack開啟監聽模式,有兩種方式

  • 啟動webpack命令時,帶上--watch引數
  • 在配置webpack.config.js中設定watch:true

缺陷:每次需要手動重繪瀏覽器

watch: true, //默認為false 不開啟
    watchOptions: { // 只有watch開啟時,watchOptions才會有意義
        ignored: /node_modules/, // 不堅挺的檔案或檔案夾,支持正則匹配
        aggregateTimeout: 300, //監聽到變化后300ms再去執行,默認300ms
        poll: 1000 //判斷檔案是否變化的輪詢間隔時間(有變化先混存,到期再更新)
    }

2.5 熱更新:webpack-dev-server(使用HotModuleReplacementPlugin插件)

wds不重繪瀏覽器

wds不輸出檔案,而是放在記憶體中

安裝依賴

npm install webpack-dev-server -D
  "scripts": {
    "dev": "webpack-dev-server --open"//有更新自動打開瀏覽器
  },

由于熱更新主要是用在開發環境中,修改mode: development

由于HotModuleReplacementPlugin是內部插件

const webpack = require('webpack')
plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
    devServer: { // 專門為webpack-dev-server指定相關配置選項
        hot: true,
        static: {
            directory: path.resolve(__dirname, "dist"),
        }
    },

運行之后會有一點報錯,點開報錯檔案修改資料就好了,

熱更新的原理

  • webpack compile:將JS變異成bundle
  • HRM Server:將熱更新的檔案輸出給HMR Runtime
  • Bundle Server:提供檔案在瀏覽器的訪問
  • HMR Runtime:會注入到瀏覽器,更新檔案的變化
  • bundle.js: 構建輸出的檔案

2.5 檔案指紋(ContentHash,ChunkHash,Hash)

檔案指紋如何生成

webpack中有三種hash可以配置

Hash: 和整個專案構建相關,只要專案檔案有修改,整個專案構建的hash值就會更改

ChunkHash: 和webpack打包的chunk有關,不同的entry會生成不同的chunkhash的值

ContentHash: 根據檔案內容來定義hash,檔案內容不變,則contenthash不變

2.5.1 js檔案的指紋設定(chunkhash在生產環境使用,無法和熱更新HotModuleReplacementPlugin一起使用)

設定output的filename,使用[chunkhash]// filename: '[name].[chunkhash].js'

新建一份生產環境專用的webpack組態檔webpack.prod.js,

設定mode: 'production',去掉熱更新相關模塊,

設定檔案指紋長度filename: '[name]_[chunkhash:8].js'

設定圖片、字體的檔案指紋

{ 
                test: /\.(png|jpeg|gif|jpg)$/,
                use: [ 
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[name]_[hash:8][ext]'
                        }
                    }]
            },
            { 
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [ 
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[name]_[hash:8][ext]'
                        }
                    }]
            }
const path = require('path')

module.exports = {
    entry: {
        'index': './src/index.js',
        'search': './src/search.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]_[chunkhash:8].js'
    },
    mode: 'production',
    module:{
        rules: [
            { 
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                }
            },
            { 
                test: /\.css$/,
                use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
                    'style-loader',
                    'css-loader',
                ]
            },
            { 
                test: /\.less$/,
                use: [ 
                    'style-loader',
                    'css-loader',
                    'less-loader',
                ]
            },
            { 
                test: /\.(png|jpeg|gif|jpg)$/,
                use: [ 
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[name]_[hash:8].[ext]'
                        }
                    }]
            },
            { 
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [ 
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[name]_[hash:8].[ext]'
                        }
                    }]
            }
        ]
    }
}

添加prod執行命令

    "prod":"webpack --config webpack.prod.js"

2.5.2 css檔案指紋設定(plugin:MiniCssExtractPlugin)

設定MiniCssExtractPlugin的filename,使用[contenthash]

// filename: '[name].[contenthash].js'

安裝依賴

npm i -D mini-css-extract-plugin

引入插件,加入plugin陣列中

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
    
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name]_[contenthash:8].css'
        });
    ]

}

同時還需要添加到依賴中,MiniCssExtractPlugin的功能是將css樣式抽離到css檔案,而style-loader是將樣式以<style>標簽的形式引入,二者有所沖突,可以去除style-loader,

{ 
                test: /\.css$/,
                use: [ //webapck use執行順序從右到左、鏈式呼叫,所以這里先執行css-loader
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                ]
            },
            { 
                test: /\.less$/,
                use: [ 
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader',
                ]
            },

2.5.2 圖片、字體檔案指紋設定

設定file-loader的filename,使用[hash]

// filename: '[name].[hash].js'

2.6 檔案壓縮 (prod環境)

2.6.1 js 檔案的壓縮(內置的uglifyjs-webpack-plugin)

const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
    
    plugins: [
        
        new OptimizeCssAssetsWebpackPlugin({
            assetNameRegExp: /\.css$/g,
            cssProcessor: require('cssnano')
        })
    ]
}

2.6.2 css 檔案的壓縮 optimize-css-assets-webpack-plugin

使用optimize-css-assets-webpack-plugin

同時使用cssnano

安裝依賴

npm i optimize-css-assets-webpack-plugin -D
npm i -D cssnano

引入依賴

const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
    plugins: [
        new OptimizeCssAssetsWebpackPlugin()
    ]
}

2.6.3 html 檔案的壓縮(html-webpack-plugin prod環境)

修改html-webpack-plugin,設定壓縮引數(模板檔案不能有注釋

安裝依賴

npm i -D html-webpack-plugin

引入html

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
            template: path.join(__dirname,'src/search.html'),// 需要打包的檔案路徑
            filename: 'search.html',
            chunks: ['search'], //
            inject: true, // 設定為true,js css會自動注入html
            minify: {

            }
        }),
        new HtmlWebpackPlugin({
            template: path.join(__dirname,'src/index.html'),// 需要打包的檔案路徑
            filename: 'index.html',
            chunks: ['index'], //
            inject: true, // 設定為true,js css會自動注入html
            minify: {//壓縮選項
                html5: true,
                collapseWhitespace: true,
                preserveLineBreaks: false,
                minifyCss: true,
                minifyJs: true,
                removeComments: false
            }
        })
    ]
}

2.7 自動清理構建目錄產物(clean-webpack-plugin)

會自動清除output目錄

安裝依賴, 引入插件


const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {

    plugins: [
       
        new CleanWebpackPlugin()
    ]
}

2.8 自動補齊css前綴(postcss-loader、autoprefixer prod環境)

安裝依賴

npm i -D postcss-loader postcss  autoprefixer

            { 
                test: /\.less$/,
                use: [ 
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader',
                    {
                        loader: 'postcss-loader',
                        
                    }
                ]
            },

新建postcss.config.js檔案進行配置

module.exports = {
    plugins: [
        require('autoprefixer')({
            overrideBrowserslist: [
                'last 2 version', 
                '>1%', 
                'ios 7']
        })
    ]
  }

2.8 移動端CSS px自動轉換成rem

px2rem-loader設定尺寸稿,

lib-flexible保證

安裝依賴

npm i -D  px2rem-loader 
//動態計算根元素單位
npm i lib-flexible -S

lib-flexible會自動在html的head中添加一個meta name="viewport"的標簽,同時會自動設定html的font-size為螢屏寬度除以10,也就是1rem等于html根節點的font-size,

配合2.91的raw-loader使用

引入使用


    module.exports = {
        
        module: {
            rules: [
                {
                    test: /\.less$/,
                    use: [
                        MiniCssExtractPlugin.loader,
                        'css-loader',
                        'less-loader',
                        {
                            loader: 'postcss-loader',
                        },
                        {
                            loader: 'px2rem-loader',
                            options: {
                                remUnit: 75, // 1rem = 75px
                                remPrecision: 8 // 轉換時的位數
                            }
                        }
                    ]
                },
                
            ]
        }
    }

在html內引入lib-flexible代碼,后期使用2.8.1介紹raw-loader引入

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="root"></div>
    <script>
        $( require('raw-loader!babel-loader./meta.html') )

        ; (function (win, lib) {
            var doc = win.document;
            var docEl = doc.documentElement;
            var metaEl = doc.querySelector('meta[name="viewport"]');
            var flexibleEl = doc.querySelector('meta[name="flexible"]');
            var dpr = 0;
            var scale = 0;
            var tid;
            var flexible = lib.flexible || (lib.flexible = {});

            if (metaEl) {
                console.warn('將根據已有的meta標簽來設定縮放比例');
                var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
                if (match) {
                    scale = parseFloat(match[1]);
                    dpr = parseInt(1 / scale);
                }
            } else if (flexibleEl) {
                var content = flexibleEl.getAttribute('content');
                if (content) {
                    var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
                    var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
                    if (initialDpr) {
                        dpr = parseFloat(initialDpr[1]);
                        scale = parseFloat((1 / dpr).toFixed(2));
                    }
                    if (maximumDpr) {
                        dpr = parseFloat(maximumDpr[1]);
                        scale = parseFloat((1 / dpr).toFixed(2));
                    }
                }
            }

            if (!dpr && !scale) {
                var isAndroid = win.navigator.appVersion.match(/android/gi);
                var isIPhone = win.navigator.appVersion.match(/iphone/gi);
                var devicePixelRatio = win.devicePixelRatio;
                if (isIPhone) {
                    // iOS下,對于2和3的屏,用2倍的方案,其余的用1倍方案
                    if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
                        dpr = 3;
                    } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
                        dpr = 2;
                    } else {
                        dpr = 1;
                    }
                } else {
                    // 其他設備下,仍舊使用1倍的方案
                    dpr = 1;
                }
                scale = 1 / dpr;
            }

            docEl.setAttribute('data-dpr', dpr);
            if (!metaEl) {
                metaEl = doc.createElement('meta');
                metaEl.setAttribute('name', 'viewport');
                metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
                if (docEl.firstElementChild) {
                    docEl.firstElementChild.appendChild(metaEl);
                } else {
                    var wrap = doc.createElement('div');
                    wrap.appendChild(metaEl);
                    doc.write(wrap.innerHTML);
                }
            }

            function refreshRem() {
                var width = docEl.getBoundingClientRect().width;
                if (width / dpr > 540) {
                    width = 540 * dpr;
                }
                var rem = width / 10;
                docEl.style.fontSize = rem + 'px';
                flexible.rem = win.rem = rem;
            }

            win.addEventListener('resize', function () {
                clearTimeout(tid);
                tid = setTimeout(refreshRem, 300);
            }, false);
            win.addEventListener('pageshow', function (e) {
                if (e.persisted) {
                    clearTimeout(tid);
                    tid = setTimeout(refreshRem, 300);
                }
            }, false);

            if (doc.readyState === 'complete') {
                doc.body.style.fontSize = 12 * dpr + 'px';
            } else {
                doc.addEventListener('DOMContentLoaded', function (e) {
                    doc.body.style.fontSize = 12 * dpr + 'px';
                }, false);
            }


            refreshRem();

            flexible.dpr = win.dpr = dpr;
            flexible.refreshRem = refreshRem;
            flexible.rem2px = function (d) {
                var val = parseFloat(d) * this.rem;
                if (typeof d === 'string' && d.match(/rem$/)) {
                    val += 'px';
                }
                return val;
            }
            flexible.px2rem = function (d) {
                var val = parseFloat(d) / this.rem;
                if (typeof d === 'string' && d.match(/px$/)) {
                    val += 'rem';
                }
                return val;
            }

        })(window, window['lib'] || (window['lib'] = {}));

    </script>

</body>

</html>

2.8.1 靜態資源行內 raw-loader

資源行內的意義

代碼層面:

  • 頁面框架的初始化腳本
  • 上報相關打點
  • css行內避免頁面閃動

請求層面:減少HTTP網路請求數

  • 小圖片或者字體行內(url-loader)

HTML和JS行內 raw-loader(讀取檔案回傳一個string,插入合適的位置)

css行內

  • 借助style-loader
  • html-inline-css-webpack-plugin

安裝依賴

npm i raw-loader@0.5.1 -D

新建src/meta.html檔案,存盤相關配置,作為靜態資源等待引入

<meta name="keywords" content="CSDN博客,CSDN學院,CSDN論壇,CSDN直播">
<meta name="description"
    content="CSDN是全球知名中文IT技術交流平臺,創建于1999年,包含原創博客、精品問答、職業培訓、技術論壇、資源下載等產品服務,提供原創、優質、完整內容的專業IT技術開發社區.">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<meta name="referrer" content="always">

修改src/index.html檔案(模板檔案不可有注釋,下列代碼注釋僅作解釋說明,不可正式使用)

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- 引入元標簽 -->
    <%=require('raw-loader!./meta.html')%>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root"></div>
    <script>
        // 引入js靜態資源
        <%=require("raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js")%>
    </script>
</body>
</html>

2.9 多頁面應用

每一次頁面跳轉的時候,后臺服務器都會回傳一個新的html,這種型別的網站也就是多頁網站,也叫做多頁應用,

每個頁面對應一個entry、一個html-webpack-plugin

缺點:每次新增或洗掉頁面需要改wenpack配置

利用glob.sync

  • entry: glob.sync(path: path.join(__dirname, './src/*/index.js'))

安裝依賴

npm i -D glob

檔案夾變動,并改動相關參考檔案路徑,

  • src/index.html=>src/index/index.html
  • src/index.js=>src/index/index.js
  • src/helloworld.js=>src/index/helloworld.js
  • src/search.html=>src/search/index.html
  • src/search.js=>src/search/index.js

修改webpack入口檔案、htmlwebpackplugins配置

const glob = require('glob')
const setMPA = ()=>{
    const entry = {}
    const htmlWebpackPlugins = []
    const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'))
    Object.keys(entryFiles).map((index)=>{
        const entryFile = entryFiles[index]
        const match = entryFile.match(/src\/(.*)\/index\.js/)
        const pageName = match && match[1]
        entry[pageName] = entryFile
        htmlWebpackPlugins.push(
            new HtmlWebpackPlugin({
                template: path.join(__dirname, `src/${pageName}/index.html`), // 需要打包的檔案路徑
                filename: `${pageName}.html`,
                chunks: [pageName], 
                inject: true, // 設定為true,js css會自動注入html
                minify: {
                    html5: true,
                    collapseWhitespace: true,
                    preserveLineBreaks: false,
                    minifyCss: true,
                    minifyJs: true,
                    removeComments: false
                }
            })
        )
    })
    return {
        entry,
        htmlWebpackPlugins
    }
}
const { entry, htmlWebpackPlugins} = setMPA()
module.exports = {
    entry: entry,
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]_[chunkhash:8].js'
    },
    mode: 'production',

    plugins: [
        
    ].concat(htmlWebpackPlugins)
}

2.10 使用source map

作用:運行代碼與源代碼之間完全不同,不方便除錯應用、定位錯誤資訊,source map就是用來映射轉換之后的代碼與源代碼之間的關系,

開發環境開啟,線上環境關閉(線上排查問題可以將sourcemap上傳到錯誤監控系統)

webpack支持12中source-map實作方式,效率和效果也不相同,

  • eval:僅能定位檔案 不生成source-map
  • eval-source-map:可以定位到檔案、行列資訊 生成source-map
  • cheap-eval-source-map:只能定位到行 生成source-map
  • cheap-module-eval-source-map:定位代碼與源代碼一模一樣
  • 特征:eval是否使用eval執行代碼
  • cheap-source-map:是否包含行資訊
  • module是否能夠得到loader處理之前的源代碼
  • inline-source-map以data-url形式嵌入代碼,會增大代碼體積
  • hidden-source-map開發第三方包使用,并沒有通過注釋的方式引入,所以瀏覽器看不到效果,
  • nosources-source-map沒有源代碼,但同樣提供了行列資訊

webpack選擇合適的source-map選擇

  • 開發模式:cheap-module-eval-source-map
  • 發布打包:不使用sourcemap,避免暴露源代碼或者使用nosources-source-map
    devtool: 'source-map'

2.11 提取公共頁面資源

思路:比如將react、react-dom基礎包通關cdn引入,不參與打包

方法:

  • 使用html-webpack-externals-plugin
  • 利用splitchunksplugin進行公共腳本分離

2.11.1 通過splitchunksplugin提取公共頁面資源

chunks引數說明

  • async同步引入的庫進行分離(默認)
  • initial同步引入的庫進行分離
  • all 所有引入的庫進行分離(推薦)

test:匹配出需要分離的包

  • minChunks:設定最小參考次數
  • minuSize:分離包的體積大小
module.exports = {

    optimization: {
        splitChunks: {
            minSize: 0,
            cacheGroups: {
                commons: {
                    name: "commons",
                    chunks: "all",
                    minChunks: 2
                },
                vendors: {
                    test: /(react|react-dom)/,
                    name: 'vendors',
                    chunks: 'all'
                }
            }
        }
    },
}

打包公共資源

新建common/index.js檔案

2.11.2 通過html-webpack-externals-plugin提取公共頁面資源

安裝依賴

npm i -D  html-webpack-externals-plugin
const htmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')

module.exports = {

        new htmlWebpackExternalsPlugin({
            externals: [
                {
                    module: 'react',
                    entry: 'https://11.url.cn/now/lib/16.2.0/react.min.js',
                    global: 'React'
                },
                {
                    module: 'react-dom',
                    entry: 'https://11.url.cn/now/lib/16.2.0/react-dom.min.js',
                    global: 'ReactDOM'
                }
            ]
        })
    ]

}

2.12 tree shaking 搖樹優化(webpack內置)

使用 webpack默認支持,在.babelre里設定modules: false即可

production mode的情況下默認開啟,測驗時設定為none

要求:必須是es6的語法,CJS不支持

DCE( dead code elimination)

代碼不會被執行,不可到達

代碼執行的結果不會被用到

代碼只會影響死變數(只寫不讀)

Tree-shaking原理

利用es6模塊的特點:

  • 只能作為模塊頂層陳述句出現
  • import的模塊名只能死字串常量
  • import binding是immutable的

代碼擦除:uglify階段洗掉無用代碼

2.13 scope hositing(webpack內置)

原理:將所有模塊的代碼按照參考順序放在一個函式作用域里,然后適當的重命名一些變數以防止變數名沖突

對比:通過scope hoisting可以減少函式宣告代碼和記憶體開銷

production mode的情況下默認開啟

在development模式下,需要手動打開該模塊

new webpack.optimize.ModuleConcatenationPlugin()

要求:必須是es6的語法,CJS不支持

2.14 代碼分割和動態加載

webpack可以將代碼庫分割成chunks(愉快),當代碼運行到需要它們的時候再進行加載,

適用的場景:

  • 抽離相同代碼到一個共享塊
  • 腳本懶加載,使得初始下載的代碼更小

懶加載JS腳本的方式

  • CommonJS: require.ensure
  • ES6: 動態import (需要babel轉換)

安裝依賴

 npm i -D  babel-plugin-syntax-dynamic-import

修改.babelra配置,使其支持動態import

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ],
    "plugins": [
        "@babel/plugin-syntax-dynamic-import"
    ]
}

修改src/search/search.js檔案

import React from 'react'
import ReactDOM from 'react-dom'
import '../css/index.less'
import fileImg from '../img/fileImg.png'
import  '../../common/index'
class Search extends React.Component{
    constructor() {
        super(...arguments)
        this.state = {
            Text: null
        }
    }
    loadComponent() {
        import('./text.js').then((Text)=>{
            console.log('Text', Text)
            this.setState({
                Text: Text.default
            })
        })
    }
    render() {
        const { Text } = this.state
        return <div className="search-txt ">search component
                {Text? <Text></Text>: null}
                測驗字體
            <img src={fileImg} onClick={()=>this.loadComponent()}></img>
        </div>
    } 
}

ReactDOM.render(
    <Search/>,
    document.getElementById('root')
)

2.15 webpack和ESLint結合

方案一:webpack與CI/CD集成

方案二:webpack與ESLint集成

2.15.1 webpack與CI/CD集成

本地開發階段增加precommit鉤子

2.15.2 webpack與ESLint集成 (打包)

安裝依賴

npm i -D eslint-config-airbnb eslint@^7.2.0 eslint-plugin-import@^2.22.1 eslint-plugin-jsx-a11y@^6.4.1 eslint-plugin-react@^7.21.5 
npm i -D eslint-loader
npm i -D babel-eslint 

修改webpack組態檔

module.exports = {

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    'babel-loader',
                    'eslint-loader'
                ]
            },
            {            
            
        ]
    },

}

新建.eslintrc.js檔案,設定eslint規則

module.exports = {
    "parser": "babel-eslint", //制定決議器
    "extends": "airbnb"
    "env": {  //JavaScript 可在檔案中使用注釋來指定環境
        "browser": true,
        "node": true
    }
    "rules": {
        "semi": "error" //結尾應當有分號
    }
}

打包,然后demo識訓一堆報錯……

2.16 webpack打包庫和組件 terser-webpack-plugin

webpack除了可以用來打包應用,也可以用來打包js庫

實作一個大整數加法庫的打包,要求:

  • 需要打包壓縮版和非壓縮版本
  • 支持AMD/CJS/ESM模塊引入

建立一個新的專案檔案夾large-number

初始化專案,并安裝依賴

npm init -y

npm i webpack webpack-cli -D

新建src/index.js檔案,存放大整數加法

export default function add(a, b){
    let i = a.length - 1
    let j = b.length - 1
    let carry = 0 // 進位
    let res = ''
    while ( i >= 0 || j>=0 ){
        let x = 0; // a某一位的值
        let y = 0; // b某一位的值
        let sum 
        if( i >= 0 ){
            x = a[i] - '0'
            i --
        }
        if( j >= 0 ){
            y = b[j] - '0'
            j --
        }
        sum = x + y + carry
        if( sum >= 10 ){
            carry = 1
            sum -= 10
        }else {
            carry = 0
        }
        res = sum + res
    }
    if( carry ){
        res = carry + res
    }
    return res
}

修改打包檔案

module.exports = {
    entry: {
        'large-number': './src/index.js',
        'large-number.min': './src/index.js'
    },
    output: {
        filename: '[name].js',
        library: 'largeNUmber', // 打包后庫的的名字
        libraryTarget: 'umd',  // 打包類別庫的發布格式,這里使用UMD
        libraryExport: 'default' // 對外暴露default屬性,就可以直接呼叫default里的屬性
    }
}

添加打包命令

    "build": "webpack"

如果就此打包的話,'large-number'、'large-number.min'均會被壓縮,需要進一步改動

npm i -D terser-webpack-plugin

修改webpack配置,設定使用環境、使用 terser-webpack-plugin插件

const terserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
    mode: 'none', // 避免全部自動壓縮,打包出未壓縮檔案
    entry: {
        'large-number': './src/index.js',
        'large-number.min': './src/index.js'
    },
    output: {
        filename: '[name].js',
        library: 'largeNUmber', // 打包后庫的的名字
        libraryTarget: 'umd',  // 打包類別庫的發布格式,這里使用UMD
        libraryExport: 'default' // 對外暴露default屬性,就可以直接呼叫default里的屬性
    },
    optimization: {
        minimize: true,
        minimizer: [new terserWebpackPlugin({
            include: /\.min\.js$/
        })],
    },
}

新建index.js檔案

if(process.env.NODE_ENV === 'production'){
    module.exports = require('./dist/large-number.min.js')
}else {
    module.exports = require('./dist/large-number.js')
}

發布npm包

npm login
npm publish

在01project專案中使用打包

npm i -D gu-large-number

直接使用

import '../../common/index'
import largeNumber from 'gu-large-number'
export function helloworld(){
    return  'hello awebpack'+largeNumber(10,20)
}

2.17 功能模塊設計和目錄結構:

2.17.1 基礎配置

  • 資源決議
  • 樣式增強
  • 目錄清理
  • 多頁面打包
  • 命令列打包和優化
  • css提取成一個單獨的檔案

2.17.2 開發階段配置

  • 代碼熱更新
  • sourcemap

通過webpack-merge

安裝依賴

npm i -D webpack-merge
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
const webpack = require('webpack')

const devConfig = {
    mode: 'development',
    plugins:[
        new webpack.HotModuleReplacementPlugin(),
    ],
    devServer: { // 專門為webpack-dev-server指定相關配置選項
        contentBase: './dist',
        hot: true,
        stats: 'error-only'
    },
    devtool: 'cheap-module-eval-source-map'
}

module.exports = merge(baseConfig, devCOnfig)

2.17.2 生產階段配置

  • 代碼壓縮
  • 檔案修改
  • tree-shaking
  • scope hositing
  • 速度優化
  • 提及優化

安裝依賴

npm i -D eslint babel-eslint eslint-config-airbnb-base

新建.eslintrc.js組態檔,配置eslint規則

module.exports = {
    "parser": "babel-eslint", //制定決議器
    "extends": "airbnb-base"
    "env": {  //JavaScript 可在檔案中使用注釋來指定環境
        "browser": true,
        "node": true
    }
    "rules": {
        // "semi": "error" //結尾應當有分號
    }
}

增加運行命令

"eslint": "eslint --fix"

三、分析webpack

3.1、使用webpack內置的stats

在package.json中設定新命令

"build:stats": "webpack --config webpack.prod.js --json > stats.json",

啟動,得到一個stats.json檔案,著實不直觀……

3.2、速度分析:使用speed-measure-webpack-plugin

可以看到每個loader和插件執行耗時

安裝依賴

npm i -D speed-measure-webpack-plugin

webpack引入

const speedMeasureWebpackPlugin = require('speed-measure-webpack-plugin')

const smp = new speedMeasureWebpackPlugin()

module.exports = smp.wrap({
})

報錯:

{
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader',
                    {
                        loader: 'postcss-loader',
                    },
                    {
                        loader: 'px2rem-loader',
                        options: {
                            remUnit: 75, // 1rem = 75px
                            remPrecision: 8 // 轉換時的位數
                        }
                    }
                ]
            },
ERROR in ./src/css/index.less
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
Error: You forgot to add 'mini-css-extract-plugin' plugin (i.e. `{ plugins: [new MiniCssExtractPlugin()] }`), please read https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started
    at Object.pitch (/Users/gujianxiang/Demo/study/webpack/01project/node_modules/mini-css-extract-plugin/dist/loader.js:43:14)
 @ ./src/search/index.js 25:0-27

暫未解決……

3.3 體積分析:使用webpack-bundle-analyzer

可以分析哪些問題?

  • 依賴的第三方模塊檔案大小
  • 業務組件大小

安裝依賴

npm i -D webpack-bundle-analyzer

修改組態檔

const webpackBundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
    plugins: [
        new webpackBundleAnalyzer(),
    ],
}

3.3 Babel-polyfill 的作用

Babel默認只轉換新的JavaScript句法(syntax),而不轉換新的API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全域物件,以及一些定義在全域物件上的方法(比如Object.assign)都不會轉碼,舉個栗子,ES6在Array物件上新增了Array.from方法,Babel就不會轉碼這個方法,如果想讓這個方法運行,必須使用babel-polyfill,為當前環境提供一個墊片,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/303066.html

標籤:其他

上一篇:Vue結合element ui 實作圖片上傳可預覽,可洗掉,以base64字串上傳到服務器

下一篇:ES6總結下

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more