Skip to content

利用webpack搭建前端开发环境,支持css,sass,image,es6,pug,webpack-dev-server等等!

Notifications You must be signed in to change notification settings

swimly/webpack-starter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Mar 31, 2017
54dbcf2 · Mar 31, 2017

History

16 Commits
Mar 30, 2017
Mar 31, 2017
Mar 31, 2017
Mar 31, 2017
Mar 31, 2017
Mar 31, 2017
Mar 31, 2017
Mar 31, 2017

Repository files navigation

webpack-starter

1、项目初始化

npm init

一路回车即可!最终会生成package.json文件,如下所示:

{
  "name": "webpack-starter",
  "version": "1.0.0",
  "description": "``` bash\r npm init\r ```\r 一路回车即可!最终会生成package.json文件,如下所示:\r ``` json",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/swimly/webpack-starter.git"
  },
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/swimly/webpack-starter/issues"
  },
  "homepage": "https://github.com/swimly/webpack-starter#readme"
}

接下来全局安装webpack。

npm i -g webpack  // 已安装的跳过

在该项目安装webpack

npm i -D webpack

package.json:

"devDependencies": {
  "webpack": "^2.3.2"
}

创建如下目录结构:

+ src
  - app.js
+ dist
package.json

app.js

console.log('hello from app.js')

到此就可以用webpack进行简单的打包

webpack ./src/app.js ./dist/app.bundle.js
webpack ./src/app.js ./dist/app.bundle.js
Hash: 59e49fa6a143615fafca
Version: webpack 2.3.2
Time: 64ms
        Asset     Size  Chunks             Chunk Names
app.bundle.js  2.66 kB       0  [emitted]  main
   [0] ./src/app.js 32 bytes {0} [built]
webpack ./src/app.js ./dist/app.bundle.js -p //打包出来的文件被压缩处理
webpack ./src/app.js ./dist/app.bundle.js -p --watch //打包出来的文件被压缩处理,并且实时监听文件的变化

这时候就可以看到dist目录下会生成一个app.bundle.js文件,呃,如果每次都这样打包,那这样还有什么乐趣可言。

接下来在根目录创建webpack.config.js

module.exports = {
  entry: './src/app.js',
  output: {
    filename: './dist/app.bundle.js'
  }
}

接下来在终端输入:

webpack

就这么简单就可以进行项目打包。 修改package.json文件如下所示:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "webpack -d --watch"  //这条是添加的
}

这时候我们只用在终端输入

npm run dev

就可以实时监听项目并且打包了! 当然开发时候是这样,实际项目打包我们还需要如下:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "webpack -d --watch",
  "prod": "webpack -p"
},

2、webpack plugins

在dist目录中创建 index.html,并且引入打包好的js文件

<!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>
  <p>content goes here</p>
  <script src="./app.bundle.js"></script>
</body>
</html>

不出意外就会看到如下的页面!

当然,如果我们不想自己手动去在dist目录创建html文件,这时候就可以利用webpack的plugin来帮我们创建。

npm i html-webpack-plugin --save-dev

修改webpack.config.js如下所示,并且删除dist目录的index.html.

var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path')
module.exports = {
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.bundle.js'
  },
  plugins: [new HtmlWebpackPlugin()]
}

然后再次运行

npm run dev

这时候你会看到dist目录里面已经自动生成一个index.html文件

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  </head>
  <body>
  <script type="text/javascript" src="app.bundle.js"></script></body>
</html>

如果我们想要根据自己的模板来创建html呢,修改webpack.config.js如下所示:

var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path')
module.exports = {
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'myApp',
      minify: {
        collapseWhitespace: true //生成被压缩的html文件
      },
      hash: true,
      template: './src/index.html', // Load a custom template (ejs by default see the FAQ for details)
    })
  ]
}

在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><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
  <p>This is my template</p>
</body>
</html>

这时候再次打包,会根据我们创建的模板来生成html文件。

3、css-loader、sass-loader为项目添砖加瓦。

css-loader
npm install css-loader style-loader --save-dev

在src目录新建app.css

html,body{
  height:100%;
  margin:0;
  background:green;
  color:#fff;
  font-size:20px;
}

修改webpack.config.js添加如下

module: {
    rules: [
      {test: /\.css$/, loaders: 'style-loader!css-loader'}
      // {test: /\.css$/, use: ['style-loader', 'css-loader']}
    ]
  },

修改app.js

import './app.css'
console.log('hello from app.js again')
sass-loader
npm install --save-dev sass-loader node-sass

修改webpack.config.js

module: {
    rules: [
      {test: /\.css$/, use: ['style-loader', 'css-loader']},
      {test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader']}
    ]
  },

修改app.js

import './main.scss'
console.log('hello from app.js again')

在src目录新建main.scss

body{background:#ff0;}

从上图我们可以看出,这样打包的样式最终都是直接加在页面的head里面,但是我们如果想以文件的方式引入进去又该如何?
npm install --save-dev extract-text-webpack-plugin

修改webpack.config.js

var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
var path = require('path')

module.exports = {
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.bundle.js'
  },
  module: {
    rules: [
      {test: /\.css$/, use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: ['css-loader']
      })},
      {test: /\.scss$/, use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: ['css-loader', 'sass-loader']
      })}
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename:  (getPath) => {
        return getPath('css/[name].css').replace('css/js', 'css');
      },
      allChunks: true
    }),
    new HtmlWebpackPlugin({
      title: 'myApp',
      minify: {
        collapseWhitespace: true //生成被压缩的html文件
      },
      hash: true,
      template: './src/index.html', // Load a custom template (ejs by default see the FAQ for details)
    })
  ]
}

详情请参考:https://www.npmjs.com/package/extract-text-webpack-plugin

4、webpack-dev-server

npm i webpack-dev-server -D

修改package.json如下:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "webpack-dev-server", // 这里是刚修改的
  "prod": "webpack -p"
},

然后

npm run dev

就可以启动这个服务器了! 修改webpack.config.js,添加如下

devServer: {
  contentBase: path.join(__dirname, 'dist'),
  compress: true,
  port: 8080,
  stats: 'errors-only',
  open: true // 启动后自动打开浏览器窗口
},

5、多模块的使用

rimraf清理项目

npm i -D rimraf

package.json修改如下:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "webpack-dev-server",
  "prod": "npm run clean && webpack -p",
  "clean": "rimraf ./dist/*"
}

这样的话会在每次打包的时候将dist目录清空,然后重新生成,以确保dist目录没有多余的无用文件。 一般情况一个项目肯定不止一个页面吧,解析来创建contact模块,修改webpack.config.js,在plugins中添加:

new HtmlWebpackPlugin({
  title: 'contact',
  hash: true,
  filename: 'contact.html',
  template: './src/contact.html'
})

并且在src根目录创建一个新的hmtl模板contact.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><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
  <p>This is contact!</p>
</body>
</html>

在src根目录新增contact.js

console.log('contact page')

由于有多个入口文件,修改webpack.config.js的entry和output

entry: {
  app: './src/app.js',
  contact: './src/contact.js'
},
output: {
  path: path.resolve(__dirname, 'dist'),
  filename: '[name].bundle.js'
},

这时候再次启动服务器,我们会发现,在index中contact.js也被调用了,这可不是我们想要的,我们只想contact.js在contact页面中被调用,修改webpack.config.js
new HtmlWebpackPlugin({
  title: 'myApp',
  hash: true,
  filename: './index.html',
  excludeChunks: ['contact'], //新增
  template: './src/index.html',
}),
new HtmlWebpackPlugin({
  title: 'contact',
  hash: true,
  filename: 'contact.html',
  chunks: ['contact'], //新增
  template: './src/contact.html'
})

这样一来,我们两个模块就互不干扰了!

6、use pug

自动生成html文件可以用html作为模板,除此之外我们还可以写jade语法,这个就需要利用pug了。 我们把src/index.html改成index.pug

doctype html
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript').
      if (foo) bar(1 + 5)
  body
    h1 Pug - node template engine
    #container.col
      if youAreUsingPug
        p You are amazing
      else
        p Get on it!
      p.
        Pug is a terse and simple templating language with a
        strong focus on performance and powerful features.

webpack.config.js

new HtmlWebpackPlugin({
  title: 'myApp',
  // minify: {
  //   collapseWhitespace: true //生成被压缩的html文件
  // },
  hash: true,
  filename: './index.html',
  excludeChunks: ['contact'],
  template: './src/index.pug', // Load a custom template (ejs by default see the FAQ for details)
}),

这时候启动我们只会看到一长串字符串,还需要安装pug-html-loader 和 pug ```bash npm i -D pug pug-html-loader ``` 当然还需要在webpack.config.js中添加一条loader
{test: /\.pug$/, use: 'pug-html-loader'}

然而事实并不如人所愿,还是有一堆bug,经过多方折腾,我们还需要安装html-loader,说干就干: ``` bash npm i -D html-loader ``` 修改webpack.config.js ``` javascript {test: /\.pug$/, use: ['html-loader', 'pug-html-loader']} ```

终于,我们见到我们想要的页面了! 接下来在src下创建 includes/header.pug
<h1>this is the header</h1>

修改index.pug

doctype html
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript').
      if (foo) bar(1 + 5)
  body
    include includes/header.pug // 新增
    h1 Pug - node template engine
    #container.col
      if youAreUsingPug
        p You are amazing
      else
        p Get on it!
      p.
        Pug is a terse and simple templating language with a
        strong focus on performance and powerful features.

这样,header就被直接引用进来了,是不是很方便呢,瞬间有种写node的赶脚有木有!

7、css,js局部刷新

伴随这项目越来越庞大,每次保存,项目都会重新打包并且自动刷新页面,虽说这样已经很爽了,但是中间那段枯燥无味的等待你是否受得了。记得刚开始接触局部刷新这个词是从ajax开始的,现在就来体验一把在开发过程中的页面局部刷新给你带来的快感。 修改我们的webpack.config.js

// 页面上面加上webpack的引用
var webpack = require('webpack')
// 在devServer加上
hot: true,
// 在plugin中加上如下两句:
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
//module改成如下:
rules: [
  {test: /\.css$/, use: ['style-loader', 'css-loader']},
  {test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader']},
  {test: /\.pug$/, use: ['html-loader', 'pug-html-loader']}
]

具体参考: https://webpack.js.org/guides/hmr-react/

最终的webpack.config.js如下所示:

var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
var path = require('path')
var webpack = require('webpack')

module.exports = {
  entry: {
    app: './src/app.js',
    contact: './src/contact.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {test: /\.css$/, use: ['style-loader', 'css-loader']},
      {test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader']},
      {test: /\.pug$/, use: ['html-loader', 'pug-html-loader']}
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 8080,
    stats: 'errors-only',
    hot: true,
    open: true // 启动后自动打开浏览器窗口
  },
  plugins: [
    new ExtractTextPlugin({
      filename:  (getPath) => {
        return getPath('css/[name].css').replace('css/js', 'css');
      },
      disable: true,
      allChunks: true
    }),
    new HtmlWebpackPlugin({
      title: 'myApp',
      // minify: {
      //   collapseWhitespace: true //生成被压缩的html文件
      // },
      hash: true,
      filename: './index.html',
      excludeChunks: ['contact'],
      template: './src/index.pug', // Load a custom template (ejs by default see the FAQ for details)
    }),
    new HtmlWebpackPlugin({
      title: 'contact',
      hash: true,
      filename: 'contact.html',
      chunks: ['contact'],
      template: './src/contact.html'
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin()
  ]
}

这时候再启动项目

#### 8、生产和发布

修改package.json

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "webpack-dev-server",
  "prod": "npm run clean && NODE_ENV=production webpack -p",
  "clean": "rimraf ./dist/*"
},

webpack.config.js

var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
var path = require('path')
var webpack = require('webpack')
var isProd = process.env.NODE_ENV = 'production';
var cssDev = ['style-loader', 'css-loader', 'sass-loader'];
var cssProd = ExtractTextPlugin.extract({
  fallback: 'style-loader',
  loader: ['css-loader', 'sass-loader'],
  publicPath: '/dist'
})
var cssConfig = isProd ? cssProd : cssDev;

module.exports = {
  entry: {
    app: './src/app.js',
    contact: './src/contact.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {test: /\.css$/, use: ['style-loader', 'css-loader']},
      {test: /\.scss$/, use: cssConfig},
      {test: /\.pug$/, use: ['html-loader', 'pug-html-loader']}
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 8080,
    stats: 'errors-only',
    hot: true,
    open: true // 启动后自动打开浏览器窗口
  },
  plugins: [
    new ExtractTextPlugin({
      filename:  (getPath) => {
        return getPath('css/[name].css').replace('css/js', 'css');
      },
      disable: !isProd,
      allChunks: true
    }),
    new HtmlWebpackPlugin({
      title: 'myApp',
      // minify: {
      //   collapseWhitespace: true //生成被压缩的html文件
      // },
      hash: true,
      filename: './index.html',
      excludeChunks: ['contact'],
      template: './src/index.pug', // Load a custom template (ejs by default see the FAQ for details)
    }),
    new HtmlWebpackPlugin({
      title: 'contact',
      hash: true,
      filename: 'contact.html',
      chunks: ['contact'],
      template: './src/contact.html'
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin()
  ]
}

这时候

npm run prod
//会出现如下错误:
'NODE_ENV' 不是内部或外部命令,也不是可运行的程序
或批处理文件。

一般如果你是mac,这样写是没有问题的,如果你是跟小编一样的穷逼还用着windows系统这样可就行不通了。

npm install --save-dev cross-env

然后修改package.json

"prod": "npm run clean && cross-env NODE_ENV=production webpack -p",

最终webpack.config.js如下所示:

var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
var path = require('path')
var webpack = require('webpack')
var isProduction = process.env.NODE_ENV === 'production';
var scssDev = ['style-loader', 'css-loader', 'sass-loader'];
var cssDev = ['style-loader', 'css-loader'];
var scssProd = ExtractTextPlugin.extract({
  fallback: 'style-loader',
  loader: ['css-loader', 'sass-loader'],
  publicPath: '/dist'
});
var cssProd = ExtractTextPlugin.extract({
  fallback: 'style-loader',
  loader: ['css-loader'],
  publicPath: '/dist'
});
var scssConfig = isProduction ? scssProd : scssDev;
var cssConfig = isProduction ? cssProd : cssDev;

module.exports = {
  entry: {
    app: './src/app.js',
    contact: './src/contact.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {test: /\.css$/, use: cssConfig},
      {test: /\.scss$/, use: scssConfig},
      {test: /\.pug$/, use: ['html-loader', 'pug-html-loader']}
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 8080,
    stats: 'errors-only',
    hot: true,
    open: true // 启动后自动打开浏览器窗口
  },
  plugins: [
    new ExtractTextPlugin({
      filename:  (getPath) => {
        return getPath('css/[name].css').replace('css/js', 'css');
      },
      disable: !isProduction,
      allChunks: true
    }),
    new HtmlWebpackPlugin({
      title: 'myApp',
      // minify: {
      //   collapseWhitespace: true //生成被压缩的html文件
      // },
      hash: true,
      filename: './index.html',
      excludeChunks: ['contact'],
      template: './src/index.pug', // Load a custom template (ejs by default see the FAQ for details)
    }),
    new HtmlWebpackPlugin({
      title: 'contact',
      hash: true,
      filename: 'contact.html',
      chunks: ['contact'],
      template: './src/contact.html'
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin()
  ]
}

最终效果如下:

10、打包图片

npm i -D file-loader
// add
{test: /\.jpg$/, use: 'file-loader'}

然后你就可以像往常一样随便的引入图片! 在html中要按如下使用:

<img src="<%= require('./img/bg.jpg')%>" alt="">
npm i -D image-webpack-loader

{
  test: /\.(png|jpe?g|svg|gif|webp)$/,
  use: [
    'file-loader?name=images/[name].[ext]',
    // 'file-loader?name=[hash:6].[ext]&publicPath=images/',
    'image-webpack-loader?{optimizationLevel: 7, interlaced: false, pngquant:{quality: "65-90", speed: 4}, mozjpeg: {quality: 65}}'
  ]
}

具体参考:https://www.npmjs.com/package/image-webpack-loader

11、babel的使用

npm i -D babel babel-preset-es2015 babel-loader babel-core
//add
{
  test: /\.js$/,
  use: 'babel-loader',
  exclude: /node_modules/
},

在根目录新增.babelrc文件

{
  "presets": ["es2015"]
}

现在我们就可以肆无忌惮的写es6的语法了!

具体参考:http://babeljs.io/docs/setup/#installation