webpack从0到1使用指南

文章同步发表在:博客地址javascript

为何要用webpack

关于为何要使用webpack,我比较认同的一种说法是:css

webpack能够很好地管理你开发中遇到的各类HTML、JS、CSS以及各类图片资源文件,同时对应不一样的资源,webpack还提供了对应的Loaders将其进行转化为适用于浏览器使用的格式html

如何从0开始上手webpack

后会无期里面,阿吕说过这么一句话:"你连世界都没有观过,哪里来的世界观",在咱们实际的项目开发中,好比你用的是vue,那么vue已经有很好的脚手架工具(vue-cli)供你使用了,或者有的开发团队,会有技术Leader专门预先作好相关的模板,方便后来新加入的成员尽快上手项目,可是随着你开发的项目越多,你可能会越不注意到那些最基本的东西,好比说这个模板究竟是如何搭建的,或者说让你本身来搭建一个模板给其余人用,是否也能作到如此的简单易上手,我想这是每个想要在前端路上进阶的人须要考虑的问题。前端

最好的方式就是本身试着去搭一个最简单的模板,从0到1的过程是最痛苦的,可是1到2或者说2到3都是水到渠成的事,下面就看看怎么开始从0搭建一个基于webpack的vue的开发环境vue

建立项目

注:使用的webpack版本为^3.10.0 首先经过webstorm或者你手上的其余IDE建立一个空的项目,我这里暂且叫proWebpack,建立好后是这个样子的:java

文件目录

由于咱们经过npm来管理包,而后每一个项目的根目录下都会有一个package.json文件来管理项目的配置信息,包括名称、版本、许可证等元数据以及记录所需的各类模块,包括 执行依赖,和开发依赖,咱们能够经过在命令控制台用npm init命令建立一个package.json文件,可是这样很麻烦,由于这样npm会经过命令行问答的方式来初始化并建立package.json文件,为了方便起见咱们使用npm init -y,这样npm就会使用一些默认值进行初始化:node

运行结果

ok如今咱们获得一个package.json文件,包含了一些简单的信息好比name、version等字段,接下来,由于咱们是想搭建一个用于vue开发环境的webpack目录结构,到目前为止还没看到vue的影子,依然是在命令行使用npm install vue --save,这里注意package.json中的依赖包有dependenciesdevDependencies两种,这两种的区别:dependencies 表示咱们要在生产环境下使用该依赖,devDependencies 则表示咱们仅在开发环境使用该依赖,咱们install时候使用--save会把包安装在dependencies 下面,因此执行完上面的命令你能够看到这样的目录结构:webpack

{
  "name": "proWebpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.5.16"
  }
}
复制代码

而后在devDependencies下面安装webpack,再次提醒,因为webpack4变化较大,这里使用^3.10.0版本的webpack: npm install -D webpack@3.10.0,此时目录结构以下:git

{
  "name": "proWebpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.5.16"
  },
  "devDependencies": {
    "webpack": "^3.10.0"
  }
}

复制代码

建立入口文件

接下来建立入口文件,在根目录下建立一个index.html做为启动页面,一个webpack.config.js做为webpack配置文件(实际项目中这里会有webpack配置文件,分别用于开发环境和生产环境,这里简便起见就用一个配置文件),新建一个src目录,在该目录下新建一个index.js做为打包入口文件:github

proWebpack
├─ index.html 启动页面
├─ package-lock.json
├─ package.json 包管理
├─ src
│    └─ index.js 入口文件
└─ webpack.config.js  webpack配置文件
复制代码

为了更接近vue-cli建立出来的模板,咱们还须要建立一个Vue实例提供给入口文件的el挂载,这个Vue文件很简单长这样:

<template>
  <div>proWebpack</div>
</template>

复制代码

而后写好入口文件:

import Vue from 'vue'
import App from '../App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})
复制代码

同时别忘记在Index.html里面要提供一个供vue实例挂载的HTMLElement实例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>proWebpack</title>
</head>
<body>
<div class="app">proWebpack</div>
</body>
</html>

复制代码

这样准备工做作得差很少了,下面作最核心的部分

webpack.config.js的编写

webpack有这么几个核心的概念:

  • entry 入口起点,webpack会找出入口起点的直接或间接依赖项,将他们进行处理输出为咱们称之为bundle的文件中
  • output 输出路径,告诉 webpack 在哪里输出它所建立的 bundles以及如何给这些buldles命名
  • loader 由于webpack自己只能理解javascript文件,因此当咱们要用vue或者React的时候可使用相关的loader(好比咱们熟知的vue-loader)来将其转换成webpack 可以处理的有效模块
  • plugins 扩展插件,能够用于打包的优化和压缩,这个可能要结合实际使用更好理解
  • module 模块 webpack中的一切你均可以理解为模块
  • chunk 代码块,一个 chunk 由多个模块组合而成,用于代码合并与分割。

下面开始配置,由于全部输出文件的目标路径必须是绝对路径,因此这里要用到使Node.js 的 path 模块

// 引入webpack和path模块
const path = require('path')
const webpck = require('webpack')
复制代码

简单配置文件的entry和output

const path = require('path')
const webpck = require('webpack')
const productionPath = require('./package.json').name

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

复制代码

这里代码应该不难理解,就是从index.js这个入口进去,把直接或间接依赖项打包输出dist文件夹目录下, 怎么运行呢,咱们须要在package.json的scripts对象里面添加咱们本身的build命令:

//  这里用最简单的命令
"build": "webpack -w",
复制代码

由于咱们的webpack配置文件就叫webpack.config.js,默认状况下,webpack在执行的时候会搜索当前目录webpack.config.js 文件执行,实际开发中咱们会使用 --config 选项来指定配置文件来对开发环境和生产环境的配置作出区分

直接在控制台npm run build,果不其然报错了:

ERROR in ./App.vue
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
| <template> | <div>proWebpack</div> | </template>
 @ ./src/index.js 2:0-28
复制代码

很明显,webpack告诉咱们他无法识别vue文件,须要咱们提供一个相关的loader来处理成webpack能够识别的文件,这里先npm install vue-loader -D,而后回到webpack.config.json配置loader:

const path = require('path')
const webpck = require('webpack')

module.exports = {
  entry: {
    index: './src/index.js'
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    // 模块配置
    rules: [
      //模块规则(配置 loader、解析器等选项)
      {
        // 这里是匹配条件,每一个选项都接收一个正则表达式或字符串
        // test是必须匹配选项
        // exclude 是必不匹配选项(优先于 test 和 include)
        // 对选中后的文件经过 use 配置项来应用 Loader,能够只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还能够分别给 Loader 传入参数。
        test: /\.vue$/,
        exclude: /node_modules/,
        use: {loader: "vue-loader"}
      }
    ]
  }
}

复制代码

配置好以后再run一遍,咦,又报错,咱们看下主要的报错信息:

ERROR in ./App.vue
Module build failed: Error: Cannot find module 'vue-template-compiler'
    at Function.Module._resolveFilename (module.js:527:15)
    at Function.Module._load (module.js:476:23)

复制代码

cannot fount那就是须要咱们去install呗,可是为何要下这个模块,我找到一个比较靠谱的说法:

其中 vue-template-compiler 是 vue-loader 的 peerDependencies,npm3 不会自动安装 peerDependencies,然而 vue-template-compiler 又是必备的,那为何做者不将其放到 dependencies 中呢?有人在 github 上提过这个问题,我大体翻译一下做者的回答(仅供参考):这样作的缘由是由于没有可靠的方式来固定嵌套依赖的关系,怎么理解这句话?首先 vue-template-compiler 和 vue 的版本号是一致的(目前是同步更新),将 vue-template-compiler 指定为 vue-loader 的 dependencies 并不能保证 vue-template-compiler 和 vue 的版本号是相同的,让用户本身指定版本才能保证这一点。查看做者的回答(英文) 。若是二者版本不一致,运行时就不会错误提示。

那只须要咱们npm i vue-template-compiler -D一下,而后在跑一遍build脚本,不出意外你应该能在项目根目录看到一个dist文件夹,把这个文件夹里面的js文件引入启动页面:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>proWebpack</title>
</head>
<body>
<div class="app">proWebpack</div>
<script src="/dist/bundle.js"></script>
</body>
</html>
复制代码

接下来咱们要访问这个页面,这里用到webpack提供的devsever来调试咱们的页面, DevServer 会启动一个 HTTP 服务器用于服务网页请求,同时会帮助启动 Webpack ,并接收 Webpack 发出的文件更变信号,经过 WebSocket 协议自动刷新网页作到实时预览。这就是为何你用vue-cli搭建出来的脚手架能够作到页面实时渲染,配置这个也很简单,在webpack.config.js里面添加以下配置:

devServer: {
    port: 3000, // 端口号
  }
复制代码

其实webpack-dev-server能够配置不少参数,这里不过多展开,接下来须要修改下咱们的build脚本:

"build": "webpack-dev-server"
复制代码

别忘了npm install一下webpack-dev-server,最后执行npm run build看到控制台报出成功信息并告诉你你的项目正运行在localhost:3000:

$ npm run build

> proWebpack@1.0.0 build D:\proWebpack
> webpack-dev-server --open

Project is running at http://localhost:3000/
webpack output is served from /

复制代码

访问localhost:3000看到以下显示的话,恭喜你,webpack配置如何从0到1你应该已经清楚了:

渲染结果

更进一步

咱们知道了怎么初步配置webpack还远远不够,实际开发中咱们会遇到更多样的状况,好比说 当咱们只有一个输出文件的时候咱们能够在output写死,就像咱们上面写的:

output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  }
复制代码

可是当咱们有多个文件输出的时候,一个个去写是一个费力不讨好的作法,这个时候就要用到webpack内置的变量了,咱们能够这么写:

output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].[hash].js'
  }
复制代码

这样会赋予每个bundle惟一的名称,而且在每次构建过程当中,生成惟一的 hash ,这时候稍微改动一下脚本文件:

"scripts": {
    "start": "webpack-dev-server",
    "build": "webpack -w",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
复制代码

以前写一块儿是为了方便理解, 如今我把跑本地服务器和打包的脚本拆开写,更语义化,通常正常开发也是这么作的,先跑一下build,你会发现此时dist文件夹下面生成了新的打包文件index.8ad46f4fb5c8385db614.js,而后把这个文件一样引入index.html,跑start脚本发现结果也是照常输出,可是又有了新的问题,若是咱们每次build以后都要手动引入带着一长串hash的js文件也是很蠢的事情,因此这里咱们能够用上一个plugin叫作html-webpack-plugin,这个插件主要有两个做用: 一、在每次编译完成以后动态为html文件引入外部资源(script、link),对于我me你这种带hash的文件名来讲无疑是极为方便的 二、能够制定一个模板的html文件,html-webpack-plugin能够根据这个模板来生成html入口文件

下面看下如何使用:

// 引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
    //省略若干代码
 plugins: [
    new HtmlWebpackPlugin({
      filename: './index.html', // 生成的入口文件的名字,默认就是index.html
      template: './index.tpl.html',// 有时候,插件自动生成的html文件,并非咱们须要结构,咱们须要给它指定一个模板,让插件根据咱们给的模板生成html
      inject: 'body',// 有四个选项值 true, body, head, false----true:默认值,script标签位于html文件的 body 底部 body:同true head:插入的js文件位于head标签内 false:不插入生成的js文件,只生成一个纯html
    })
  ],
复制代码

用到的index.tpl.html只是和以前的index.html做了一点小小的区分,让人知道这是模板生成的:

// index.tpl.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>这是模板生成的</title>
</head>
<body>
<div class="app">proWebpack</div>
</body>
</html>
复制代码

方便起见咱们在package.json在添加一个clean的脚本,每次build以前先删除以前生成的dist文件:

"scripts": {
    "start": "webpack-dev-server",
    "build": "npm run clean && webpack -w",
    "clean": "rimraf dist", // 添加clean脚本
    "test": "echo \"Error: no test specified\" && exit 1"
  }
复制代码

而后run build一下:

模板生成的结果
咱们发现这个时候的入口文件已是根据模板自动生成的了,并且自动把生成的js文件添加到了入口文件里面,同时npm run start命令看到的界面也是使用模板生成的页面,这就大功告成了。

最后

其实这里还有一点没有提到的就是css的处理,除了css-loader咱们日常开发还会用到一些css预处理器如scss,还有postcss等样式处理工具,这里的处理其实也不难,感兴趣的朋友能够本身尝试一下,这里因为篇幅缘由(我想偷懒)就不过多展开了。

相关文章
相关标签/搜索