Webpack打包文件分类与url修正(原理讲解)

在原来的文章中我们把打包的东西都存放到了dist目录下,并没有进行分类存储,乱成一团,现在我们需要处理一下打包的路径,让打包后的目录看起来更加优雅。

代码准备

我们先建立起这样一个目录结构

├── node_modules
├── src
    | ├── assets
        | └── css
            | └── index.css
        | └── img
            | └── noding.jpg
        | └── js
            | └── index.js
    | └── index.html
├── .babelrc
├── package-lock.json
├── package.json
├── webpack.config.js
└── dist

我们在src/index.html中输入一段代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="box"></div>
    <i class="fa fa-bath" aria-hidden="true"></i>
    <i class="fa fa-envelope-open" aria-hidden="true"></i>
    <i class="fa fa-microchip" aria-hidden="true"></i>
    <i class="fa fa-user-circle-o" aria-hidden="true"></i>
</body>
</html>

接下来在assets/js/index.js中输入下面代码

// 引入icon图标字体
import "font-awesome/css/font-awesome.css"
// 引入图片
import imgSrc from '../img/nodeing.jpg'
// 把图片插入到html文件中
document.getElementById("box").innerHTML = '<img src="'+imgSrc+'" />'

然后去webpack.config.js中去修改一下入口文件位置
接下来,我们去测试一下打包效果

npm run dev

所有文件都打包到dist目录下,接下来,我们需要去优化webpack配置,让打包出来的文件不那么混乱

webpack配置

把js文件分类

我们希望打包出来的文件也像src目录一样能够分门别类的存放,首先,我们把本来的js单独放进assets/js文件夹下,这时我们需要修改webpack配置文件,把输出目录改掉

output: {
    path: path.resolve(__dirname, 'dist/assets'),
        //上面这个path代表webpack默认会把所有的文件都打包进assets文件夹中
        //上面这个是一个所有打包文件输出位置的公用路径
    filename: 'js/my-first-webpack.bundle.js'
},

运行npm run dev 查看效果,发现dist目录下多出了一个assets文件,并且已经建立好了js文件夹,
app.js也被放进来了,但是,原来打包出来的那些文件还在,这时候,dist目录就更乱了,我们希望每次打包生成的文件都是最新的,得手动去删除上次打包出来的文件,像这种手动删除dist目录的操作,
我们可以交给webpack相关插件来完成,这个插件叫clean-webpack-plugin
安装clean-webpack-plugin插件

npm install clean-webpack-plugin --save

修改webpack配置文件,引入clean-webpack-plugin插件
webpack.config.js

const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
// 引入clean-webpack-plugin插件*************************************
const CleanWebpackPlugin = require("clean-webpack-plugin")
module.exports = {
    entry: "./src/assets/js/index.js",
    output: {
        path: path.resolve(__dirname, 'dist/assets'),
        filename: 'js/app.js'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
    }),
// 使用插件,设置需要清除的目录*******************************************
        new CleanWebpackPlugin(['dist'])
        ],
    devServer: {
        open: true
    },
    module: {
        rules: [
            {
            test: /\.js$/,
            use:['babel-loader'],
            exclude: path.resolve(__dirname, 'node_modules')
        },
        {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
        },
        // 处理文字
        {
            test: /\.(eot|svg|ttf|woff|woff2)$/,
            use: 'file-loader'
        },
        {
            test: /\.(jpg|png|gif|webp|bmp)$/,
            use: [{
                loader: 'url-loader',
                options: {
                limit: 10240
                }
            }]
        }
    ]
}
}

这个时候你会发现,每次运行npm run dev,这个插件就会先把上次的dist目录删除,然后再新建一个dist目录,把新打包的文件放里面
以上步骤,我们把js文件放到了assets/js目录下,但随之而来的就是,所有的文件都被打包到assets这个目录下了,我们不希望index.html这个文件也被打包进assets目录下,而是像在src目录下一样,它们应该是同级的,所以我们需要修改一下 index.html被输出的路径

plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html',
        // 这里表示往外回退一层
        filename: '../index.html'
    }),
    new CleanWebpackPlugin(['dist'])
],

把字体文件分类

修改webpack配置文件,给字体文件增加输出规则

// 处理文字 
{
    test: /\.(eot|svg|ttf|woff|woff2)$/,
    use: [{
        loader: "file-loader",
        options: {
            name: 'fonts/[name]_[hash:4].[ext]'
        }
    }]
},

其中,[name]、[hash]、[ext]都是可变的,name表示原来的文件名字,hash表示生成的hash值,
hash:4表示可以限定hash字符串的位数,ext表示原来文件的扩展名(文件后缀)

把图片文件分类

修改webpack配置文件,给图片文件增加输出规则

{
    test: /\.(jpg|png|gif|webp|bmp)$/,
    use: [{
        loader: 'url-loader',
        options: {
            limit: 10240,
            name: 'img/[name]_[hash:4].[ext]'
        }
    }]
}

因为,ulr-loader是对file-loader的封装,其中的[name]、[hash]、[ext]表示的意思和file-loader中表示的意思是一样的

把css文件分类

css文件是打包进js文件的,如果需要单独打包出来,需要安装另一个插件,extract-text-webpack-plugin

npm install extract-text-webpack-plugin --save-dev

这个插件的使用规则和其他plugin差不多,首先需要引入插件
webpack.config.js

var ExtractTextWebpackPlugin = require("extract-text-webpack-plugin")

其次,是需要实例化对象,并且传入配置项,然后将实例化出来的这个对象加入到plugins选项中

var extractcss = new ExtractTextWebpackPlugin({
    //打包输出的路径↓
    filename: 'css/index.css'
})
plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html',
        filename: './index.html'
    }),
    new CleanWebpackPlugin(['dist']),
    // 这⾥是前面实例化得到的对象↓
    extractcss
],

最后,需要修改css输出规则

{
    test: /\.css$/,
    use: extractcss.extract({
        fallback:'style-loader',
        use:['css-loader']
    })
},

修正url地址

先分析一下webpack.config.js中的devServer

devServer:{
    //当服务器启动的时候,自动帮助我们打开浏览器
    open: true,
        //服务器启动的端口地址
    port: 8090,
        //访问的本地目录是哪个
    contentBase: './dist'
}

上面所有的webpack配置好了之后,我们在终端用npm start启动服务时,发现根本访问不了dist文件,因为
1.当服务启动的时候,CleanWebpackPlugin这个插件的配置,就会把dist目录删除
2.而npm start并不会再次生成dist,但是内存中是有dist的,为什么访问不了?
解决办法
当服务启动的时候,wenbpack-dev-server会把资源打包到内存
npm start会优先访问内存中output.path:path.resolve(__dirname,'dist/assets')设置的输出路径,而为什么访问不了内存中的资源?
举例:当我们在浏览器中输入http://localhost:8090/ ,访问的是内存中的dist/assets
当我们在浏览器中输入http://localhost:8090/js/my-first-webpack.bundle.js,访问的是内存中的dist/assets/js/my-first-webpack.bundle.js.js
这样的话,我们发现,我们只可以访问到assets目录下的内容,却访问不到和assets同级的内容
当我们把各种类型的文件分类好以后,运行打包出来的index.html,发现图片、字体、css等的路径是不正确的,没办法正常工作,这个时候,我们需要用output配置项下的 publicPath来修正一下

output: {
    path: path.resolve(__dirname, 'dist/assets'),
    filename: 'js/app.js',
    publicPath: 'assets/'
},

其中,publicPath设置的是所有静态资源的基础目录,要快速理解它,可以记住下面的公式:
静态资源最终访问路径 = output.publicPath + 资源loader或插件等配置路径

举例说明,我们在各种loader中设置的路径如下:

// css输出路径
name: 'css/[name]_[hash:4].[ext]'
// 图片输出路径
name: 'img/[name]_[hash:4].[ext]'
// 字体文件输出路径
name: 'fonts/[name]_[hash:4].[ext]'
// js文件输出路径
filename: 'js/app.js'

设置的output.publicPath为 "assets/", 那么最终的访问路径就是output.publicPath+loader设置的路径,例如图片路径就会是 assets/img/[name]_[hash:4].[ext]
devServer.publicPath这个选项是webpack-dev-server服务器的访问路径,我们知道当webpack-dev-server启动后,会把资源打包到内存,你不需要关注它在内存中的什么地方,你可以理解为打包后的资源也被输出了,只是输出到内存中,你无法在硬盘上看到而已,但是它提供了一个地址来供我们访问,devServer.publicPath就是用来设置访问内存中被打包出来的资源地址的,举例说明:

output: {
    path: path.resolve(__dirname, 'dist/assets'),
    filename: 'js/my-first-webpack.bundle.js',
    },
devServer: {
    open: true,
    publicPath: '/aa'
},
其他配置省略...

像上面这种配置,当服务器启动的时候,如果想访问到内存中的my-first-webpack.bundle.js,那么需要通过http://localhost:8090/aa/js/my-first-webpack.bundle.js
当前的完整的webpack配置是这样的

const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
module.exports = {
    entry: "./src/assets/js/index.js",
    output: {
        path: path.resolve(__dirname, 'dist/assets'),
        filename: 'js/my-first-webpack.bundle.js',
        //资源的基础路径,设置什么值就会在原来的路径前面加上这个值
        //注意:不是在打包输出的文件夹前面加,而是资源在最终访问的时候加
        publicPath: 'assets/'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: '../index.html'
        }),
        new CleanWebpackPlugin(['dist'])
        ],
        devServer: {
            open: true,
            containtBase: 'src/'
            publicPath: '/aa'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use:['babel-loader'],
                exclude: path.resolve(__dirname, 'node_modules')
            },
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        name: 'css/[name]_[hash:4].[ext]'
                    }
                }
            ]
        },
        // 处理⽂字
        {
            test: /\.(eot|svg|ttf|woff|woff2)$/,
            use: [{
                loader: "file-loader",
                options: {
                    name: 'fonts/[name]_[hash:4].[ext]'
                }
            }]
        },
    {
            test: /\.(jpg|png|gif|webp|bmp)$/,
            use: [{
                loader: 'url-loader',
                options: {
                    limit: 10240,
                    name: 'img/[name].[ext]'
                }
            }]
        }
    ]
    }
}

我们来解读几条关键的配置:
1.output.path: path.resolve(__dirname, 'dist/assets'), 这条配置是打包后资源输出的目录,
同样的当webpack-dev-server启动后,资源会打包到内存,用不太严谨但通俗的话来说你可以认为所有文件被打包到这个目录,只是这个目录放在内存而不是输出到硬盘,你访问这个目录里面的文件的时候,路径和打包到硬盘是看到的路径一样,例如:在硬盘上 dist/assets/js/app.js, 在内存中也通过这种目录结构访问就是了
2.output.publicPath: 'assets/',是静态资源的基础路径
3.devServer.publicPath: '/aa', 这个配置是服务器访问内存的虚拟路径, http://localhost:8080/aa 这个地址,你可以认为是映射到了output.path设置的目录,不太严谨但通俗的说,你可以认为http://localhost:8080/aa这个地址指向了这个目录(dist/assets/),那么你在http://localhost:8080/aa后面加 /js/app.js 就相当于访问 dist/assets/js/app.js,所以启动以后通过http://localhost:8080/aa/js/app.js是可以访问到这个js文件的
4.devServer.contentBase设置的是本地目录(在内存中找不到的时候去硬盘上找)
5.filename: '../index.html',这个在HtmlWepackPlugin中的配置,设置了被打包出来的html位置,../表示往上一层返回,那就意味着,最终打包出来的结构是这样的:

├── dist
| ├── assets
|   ├── css
|   ├── img
|   ├── js
| ├── index.html
|

我们通过http://localhost:8080/aa (devServer.publicPath属性定义的映射)能访问到dist/assets目录,也可以访问该目录下层级更深的文件,只需要在http://localhost:8080/aa 后面加文件路径就可以了,但是却永远无法返回到上层目录去访问到index.html
当访问不到内存中的index.html⽂件,webpack-dev-server就会去访问本地的路径,这个本地的路径就是devServer.containtBase: 'src/'这个选项设置的,所以npm start的时候,直接打开的是项目下的src目录
好了,问题原因已经找出来了,接下来我们需要修改以下webpack配置,具体的思路就是

    1. 输出目录往上挪动一层 output.path 设置成 path.resolve(__dirname, 'dist'),
    1. 在每个loader上加深一层 js加深一层是这样的 assets/js/app.js 图片加深一层是这样的 assets/img/...,其他,同理
    1. ../index.html 改为当前目录 ./index.html
    1. output.publicPath的值设置成和devServer.publickPath的值一样,不然静态资源的访问地址会出错,返回404

这样做,原来打包输出的目录变成了dist目录,而不是原来的dist/assets目录, 那么http://localhost:8090/aa 这个地址指向的内存虚拟目录就是 dist目录,那么通过http://localhost:8090/aa/index.html 就可以访问到内容了
具体完整配置如下:

const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
module.exports = {
    entry: "./src/assets/js/index.js",
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'assets/js/my-first-webpack.bundle.js',
        publicPath: '/'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: './index.html'
        }),
        new CleanWebpackPlugin(['dist'])
    ],
    devServer: {
        open: true,
        publicPath: '/'
    },
    module: {
    rules: [
        {
            test: /\.js$/,
            use:['babel-loader'],
            exclude: path.resolve(__dirname, 'node_modules')
        },
        {
            test: /\.css$/,
            use: [
            'style-loader',
        {
            loader: 'css-loader',
            options: {
                name: 'assets/css/[name]_[hash:4].[ext]'
            }
        }
    ]
},
// 处理文字
{
    test: /\.(eot|svg|ttf|woff|woff2)$/,
    use: [{
        loader: "file-loader",
        options: {
            name: 'assets/fonts/[name]_[hash:4].[ext]'
            }
        }]
    },
{
        test: /\.(jpg|png|gif|webp|bmp)$/,
        use: [{
            loader: 'url-loader',
            options: {
                limit: 10240,
                name: 'assets/img/[name].[ext]'
                }
            }]
        }
    ]
}
}

通常output.publicPath和devServer.publicPath设置成 "/",这样我们就可以通过 http://localhost:8090/ 来访问打包出来的index.html文件了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容