npm + webpack + es6 使用

webpack官网:https://www.webpackjs.com/

准备

  1. 下载Node.jsnpm
  2. 一个命令行工具。不是必须的,用自带的命令行也可以。
  3. 创建一个文件夹,作为根目录,比如 npm-webpack

这时,你将看到一个空文件夹

开始

  1. 命令行打开至根目录
  2. 键入 npm init,一路确定到yes ————————创建一个package.json。

image-20201010094917084

安装webpack

安装webpack前,先附上几个常用的npm命令

npm init      这个指令会引导你创建一个package.json,包括版本作者等信息,有助于你发包。后面安装的包的依赖关系也会在package.json里有体现。
npm install     直接执行这个命令,会按照当前目录下的package.json的配置去安装各个依赖的包。
npm install [module]    在当前目录安装这个模块。会去检测该模块是否存在于node_module文件夹中,存在了就不安装了。 
npm install [module] -g    在全局进行模块安装。全局模式下安装的包,会自动注册到系统变量 path里的。
npm install [module] --save-dev    在当前目录下安装这个模块,但是仅在开发时使用。在package的"devDependencies"下,表示仅在开发的时候使用。

有一些包是需要用命令行的,这些需要注册系统变量,因此像supervisor等包,一定要安装在全局。否则就不能用它的命令行指令。
有一些包是在js中使用的,那么这些包安装到当前目录就可以了。
webpack 一般建议全局安一个,当前目录安一个。

我们刚才已经使用了npm init创建了一个package.json,接下来我们开始安装webpack。

首先在全局安装一个webpack

执行 npm install webpack -g         (已经全局安装webpack 的可以跳过这一步)

然后在当前目录下安装一个webpack

执行 npm install webpack --save

你会发现当前目录下新增了一个文件夹node_module,在里头有着webpack的包

检验下,webpack 安装成功了没

执行 webpack -v

webpage-cli

这时候如果出现了

image-20201010102912528

说明你安装的是webpack 4X 后的版本,这里提示安装 webpack-cli// 是因为到了webpack4, webpack 已经将 webpack 命令行相关的内容都迁移到 webpack-cli,所以除了 webpack 外,我们还需要安装 webpack-cli

输入yes之后,你会发现 再次输入webpack -v,它还会让你安装,这是因为这是在局部安装的,我们需要全局安装

//因为上面的webpack是全局安装的,因此这里我们安装weback-cli也是需要全局安装的!
npm install --save-dev webpack-cli -g

如果webpack安装成功了,就会在命令行打印出webpack的版本

image-20201010103916028

使用webpack来组织文件

在直接介绍使用es6模块化语言来组织文件之前,我们先了解一下webpack的使用。
webpack会将我们用模块化语言语法写成的源文件,编译成浏览器可识别的文件。也就是有从源文件→线上文件的过程。

我们来实践一下:

  • 首先在根目录下创建一个文件夹src来放源文件;
  • 再创建一个文件夹dist来放编译后文件;
  • 新建一个html文件来放html文件
  • 最后创建一个webpack.config.js文件。 (先创个空的,待会儿加内容)

这时你的目录结构将如下:

image-20201010104653496

webpack.config.js是webpack的配置文件。
要搞懂webpack其实就是要懂得怎么来配置 webpack.config.js。
本文介绍一个基础的配置,完整的配置教程请参照官网文档——webpack官网文档

接下来:

  • 在src中新建一个文件—— sourceFile.js

    文件内容,随意点:

//sourceFile.js
console.log('我是superman');

image-20201010105422160

  • 配置 webpack.config.js (关键一步)
module.exports = {
    entry:{
        bundle : __dirname + '/src/sourceFile.js' 
    },
    output:{
        path: __dirname + '/dist',
        filename: '[name].js'
    }
}

这个文件仅有entry和output,应该是最简单的配置文件了。

  1. module.exports 是CommonJS的写法,这个是Node.js的规范
  2. __dirname 代表当前目录,Node.js会去识别
  3. entry中,值为一个路径,代表源文件的存放位置。键,则可以随便取,在我的配置中,编译后文件的名字就是这里的键。
  4. output中,path为编译后的文件存放的文件夹。 filename 为编译后文件夹名字。 其中[name]代表了entry中的键。也就是说上面是什么名字,编译后就是什么名字。可以自己试验下。

使用webpack进行编译

在命令行键入 webpack -w  

这时候命令行出现

image-20201010110338478

这是一个警告,大概的意思就是说:没有“mode”选项还没有设置,webpack将返回到“production”。将“模式”选项设置为“开发”或“生产”,以启用默认设置。那mode有什么用,怎么用?接着往下看

webpack mode

webpack的 mode 配置用于提供模式配置选项告诉webpack相应地使用其内置的优化,mode有以下三个可选值

  • development 开发模式
  • production 生产模式
  • none 不使用任何模式

配置

1. 直接写在webpack.config.js配置中

module.exports = {
  mode: 'production'
};

2. 作为webpack执行的参数

webpack --mode=production

通过上面的配置,我们就可以在业务代码中通过process.env.NODE_ENV拿到环境变量值,这里的process.env.NODE_ENV 要跟node的区分,这句等同于

new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),

如果我们在webpack执行命令和webpack配置文件里都没有写入mode配置,在执行webpack时会有如下提示:在没有配置的情况下默认显示production,这里我们建议大家配置,这样才有区分环境的意义

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/

使用

在开发和生产版本有很多不同之处,主要可分下面几种

  • 接口不同,后端返回的接口分线上开发
  • 编译结果不同,是否分离js,css,是否压缩等

通过mode的设置,我们就可以轻松对开发环境做严格的区分

1.运用于开发和生产的接口区分

package.json 配置

{
 "scripts": {
    "dev": "webpack-dev-server --mode=development --devtool inline-source-map --hot",
    "build":"webpack --mode=production",
  },
}

接口前缀根据编译的mode值区分

// 接口前缀配置
let baseUrl = "";
const env = process.env.NODE_ENV;
if(env === "production" || env === "none"){
	baseUrl= "https://www.production.com/public/"; 
}else{
	baseUrl= "https://www.development.com/public/"; 
}
export default baseUrl;

2.运用在编译打包

这是webpack4改进很重要的一点,开发者不需要太多配置,只需要设置好mode,webpack会根据mode在编译打包时执行不同的操作优化,具体参考官方文档

改进webpack4 编译命令

这时候我们将,编译的命令改下

#执行下面命令表示不使用任何模式 
webpack --mode=none -w

执行上面的命令之后,dist中会新出现一个 bundle.js, 代码长这个样子:

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

console.log('我是superman');

/***/ })
/******/ ]);

可以看到编译后的js多了很多额外的内容,所以如果项目小的话是不需要模块化的。模块化是用来构建中大型项目的。并且这是没有指定模式的,如果切换到生产模式,会打包的更小注释基本没有

测试js引入

来看看效果

  • 在html文件夹下新建一个 test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>看看我们编译后的js可不可以用</title>
</head>
<body>
    <script src = "../dist/bundle.js"></script>
</body>
</html>

在浏览器打开test.html,你会看到浏览器的console中:

image-20201010113246771

说明我们将源文件sourceFile.js编译后生成的bundle.js,是可以正常使用的。

疑惑

这样子做的话,和html中直接引用源文件效果是一样的啊。话说为什么要编译啊?这样不是更麻烦吗?

这是我刚接触webpack的感受。后来,我逐步理解了,编译其实是为了实现模块化。基于AMD/CMD/CommonJS/es6的语法,浏览器是无法识别的。这些规范的语法,你可以感受一下:

 //AMD
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC)
{
    alert('加载成功');
});

//CMD
seajs.use("../static/hello/src/main")

//CommonJS
module.export = {
    name:'rouwan'
}

//es6模块
import {module1, module2} form './module.js';

这些规范使用的语法,有的浏览器是不能识别的。不信你试一下,立马报错。好像谷歌支持的好点但也不是全部支持。因此,需要由webpack来编译,编译后的文件,浏览器能够识别。现在,我们开始使用es6模块语法来组织模块吧

使用es6模块语法

webpack可以支持es6语法。这个也是为什么webpack强大的原因。用es6a ,想想就爽。
当然,我们需要先下载配置babel

webpack 4.x 之前 babel使用

下载和配置babel
下载babel:

//下载babel的webpack加载器
npm install --save-dev babel-loader babel-core babel-preset-es2015  

下载完了,要去webpack.config.js进行配置,配置完的文件如下

module.exports = {
    entry:{
        bundle : __dirname + '/src/sourceFile.js'
    },
    output:{
        path: __dirname + '/dist',
        filename: '[name].js'
    },
    module:{
        loaders:[{
            test: /\.js$/,
            loader: 'babel?presets=es2015'
        }]
    }
}

可以看到,和之前的webpack.config.js相比,增加了一个loaders的配置。
大致意思是:所有的js文件,使用babel加载器来翻译一下
具体配置原理可查官网文档 loader的api

怎么看自己是否配置好呢?待会儿webpack编译时看有没有报错,浏览器端有没有识别es6语法就知道了。

测试使用es6模块

在src文件夹下新建一个文件——moduleTest.js

//moduleTest.js
function say(){
    console.log('我引用了一个模块')
}

export {say}

将sourceFile.js的内容改为:

//sourceFile.js
import {say} from './moduleTest.js';
say();

在命令行运行webpack编译指令

#编译转化
webpack --mode=none -w

如果没有报错,则可以接着执行,但是我出现了,这说明我的webpack是4.x之后的,4.x之前的这样是可以的

image-20201010115231120

实际上出错信息已经说明了问题原因:

Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.

这一段的意思是webpack.config.js错误,原因是这个配置文件的版本和我们当前安装的webpack的版本不匹配。

configuration.module has an unknown property ‘loaders’.

接下来这段我们只需要看前面一句,意思是webpack.config.js这个配置文件里的module属性有一个未知的配置项loaders,原因嘛,就是我们当前安装的webpack版本已经去掉了这个配置。

webpack 4.x babel使用

  • 安装 babel-loader@babel/core@babel/preset-env
npm i babel-loader @babel/core @babel/preset-env -D
  • webpack.config.js
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/, // 忽略node_modules里的js文件
                loader: "babel-loader",
                options: {
                    presets: ["@babel/preset-env"]
                }
            }
        ]
    }

这时候的配置文件是这样

module.exports = {
    entry:{
        bundle : __dirname + '/src/sourceFile.js'
    },
    output:{
        path: __dirname + '/dist',
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                // 忽略node_modules里的js文件
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    presets: ["@babel/preset-env"]
                }
            }
        ]
    }
}
  • 上面的设置能够在打包时把es6转为es5,但不包括es6的一些新特征,比如promise
  • 解决上面问题的方法是,安装 @babel/polyfill@babel/polyfill会添加很多代码,而非按需加载。
npm i babel-loader @babel/polyfill -D
  • 实现按需加载的配置 webpack.config.js

一步到位的安装

npm i babel-loader @babel/core @babel/preset-env @babel/polyfill -D
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    presets: [[
                        "@babel/preset-env",
                        {
                            useBuiltIns: "usage"
                        }
                    ]]
                }
            }
        ]
    }

这时候配置文件是这样的

webpack.config.js

module.exports = {
    entry:{
        bundle : __dirname + '/src/sourceFile.js'
    },
    output:{
        path: __dirname + '/dist',
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    presets: [[
                        "@babel/preset-env",
                        {
                            useBuiltIns: "usage"
                        }
                    ]]
                }
            }
        ]
    }
}

package.json

{
  "name": "npm-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "webpack": "^4.44.2"
  },
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/polyfill": "^7.11.5",
    "@babel/preset-env": "^7.11.5",
    "babel-core": "^6.26.3",
    "babel-loader": "^8.1.0",
    "babel-preset-es2015": "^6.24.1",
    "webpack-cli": "^3.3.12"
  }
}

重新执行打包

webpack --mode=none -w

image-20201010141625085

运行成功

这时候我们再打开test.html看看,

image-20201010142007483

可以看到成功的执行

不支持class的问题

当我们使用了一些JavaScript的一些新特性的时候,但是有没有在webpack.config.js里面或者是.babelrc文件中配置相关插件,就会出现

Support for the experimental syntax 'classProperties' isn't currently enabled

image-20201010150137477

解决方案:安装如下插件

npm i @babel/plugin-proposal-class-properties -D

最新的一步到位

>npm i babel-loader @babel/core @babel/preset-env @babel/polyfill @babel/plugin-proposal-class-properties -D

配置文件中添加

plugins: [
	"@babel/plugin-proposal-class-properties",
]

这时候的配置文件中options选项中加载插件

module.exports = {
    entry:{
        bundle : __dirname + '/src/ClassAll.js'
    },
    output:{
        path: __dirname + '/dist',
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    presets: [[
                        "@babel/preset-env",
                        {
                            useBuiltIns: "usage"
                        }
                    ]],
                    plugins: [
                        "@babel/plugin-proposal-class-properties"
                    ]
                }
            }
        ]
    }
}

参考:

https://segmentfault.com/a/1190000006968235

https://www.webpackjs.com/concepts/mode/#用法

https://blog.csdn.net/lqlqlq007/article/details/80091899

https://segmentfault.com/a/1190000021882274

posted @ 2020-10-10 14:23  makalo  阅读(1604)  评论(0编辑  收藏  举报