項目github地址javascript
上一篇主要講述了npm package的發佈、更新、刪除、開發過程當中的調試,以及擁有一個私有庫的幾種方式,這篇來說講怎麼把咱們寫的代碼編譯打包(即各類語法轉換成ES5)出來後,各個環境(瀏覽器、node)均可以使用,且不侷限引用方式,便可以用ES6的import,node的require,以及script標籤。咱們先從babel入手。css
babel is a JavaScript compilerhtml
Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments. Here are the main things Babel can do for you:前端
——摘抄 babeljava
The entire process to set this up involves:node
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill複製代碼
2. Creating a config file named babel.config.json
in the root of your project with this content:react
{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
"useBuiltIns": "usage",
}
]
]
}複製代碼
3. And running this command to compile all your code from the src
directory to lib
:jquery
./node_modules/.bin/babel src --out-dir lib複製代碼
You can use the npm package runner that comes with npm@5.2.0 to shorten that command by replacing
./node_modules/.bin/babel
withnpx babel
webpack
——摘抄 babel 指南-Usage Guidegit
【提問:我想要在組件庫中使用ES6/7/8/9等等最新的javascript語法,但是瀏覽器不兼容怎麼辦?】
@babel/preset-env
is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s). This both makes your life easier and JavaScript bundles smaller!
npm install --save-dev @babel/preset-env
yarn add @babel/preset-env --dev複製代碼
【提問:個人組件庫是用react寫的,react又要怎麼轉換呢?】
This preset always includes the following plugins:
And with the development
option:
——摘抄 babel presets-react
npm install --save-dev @babel/preset-react
yarn add @babel/preset-react --dev複製代碼
【小白提問:我打算用TypeScript來寫個人組件庫,避免我編程的時候犯的一些低級錯誤,對組件使用者也相對更友好一些,那ts又須要用什麼轉換呢?】
This preset includes the following plugins:
You will need to specify
--extensions ".ts"
for@babel/cli
&@babel/node
cli's to handle.ts
files.
npm install --save-dev @babel/preset-typescript複製代碼
// presets逆序執行(從後往前)ts -> react -> ES6/7
// preset的參數怎麼寫,有哪些,請自行查閱官方文檔,這裏不展開
{
"presets": ["@babel/preset-env","@babel/preset-react","@babel/preset-typescript"]
}複製代碼
Now luckily for us, we're using the env
preset which has a "useBuiltIns"
option that when set to "usage"
will practically apply the last optimization mentioned above where you only include the polyfills you need. With this new option the configuration changes like this:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage", // https://www.babeljs.cn/docs/usage#polyfill
}
],
"@babel/preset-react",
"@babel/preset-typescript"
]
}複製代碼
【筆者理解】簡單的來講,useBuiltIns
設置爲usage
,babel會自動import對應的modules,簡單方便。參考
// In a.js
var a = new Promise();
// Out (if environment doesn't support it) import "core-js/modules/es.promise"; var a = new Promise(); // Out (if environment supports it) var a = new Promise();複製代碼
編譯裝飾器
Simple class decorator
@annotation
class MyClass { }
function annotation(target) {
target.annotated = true;
}複製代碼
若是legacy
字段設爲true
的話,就要配合@babel/plugin-proposal-class-properties使用,且loose
要設置爲true
,參考
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}複製代碼
A plugin that enables the re-use of Babel's injected helper code to save on codesize.
Instance methods such as
"foobar".includes("foo")
will only work withcore-js@3
. If you need to polyfill them, you can directly import"core-js"
or use@babel/preset-env
'suseBuiltIns
option.
The plugin transforms the following:
var sym = Symbol();
var promise = Promise.resolve();
var check = arr.includes("yeah!");
console.log(arr[Symbol.iterator]());複製代碼
into the following:
import _getIterator from "@babel/runtime-corejs3/core-js/get-iterator";
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
import _Symbol from "@babel/runtime-corejs3/core-js-stable/symbol";
var sym = _Symbol();
var promise = _Promise.resolve();
var check = _includesInstanceProperty(arr).call(arr, "yeah!");
console.log(_getIterator(arr));複製代碼
——摘抄 babel 用法-transform-runtime
【筆者理解】能夠自動引入對應Babel's injected helper code,同use @babel/preset-env
's useBuiltIns
option
基礎配置ok了,其餘的語法須要babel解析的話,能夠再自行查找babel-plugins。
(說一下筆者的操做,先一頓狂寫,而後編譯一下,babel會報錯,報啥錯,就安裝啥插件,簡單粗暴。每次的錯誤都要用心記錄下來哦,這樣之後就能夠提早安裝好須要的各類babel plugins了)
ES6 Module的加載實現(比較了ES6和CommonJs的差別、循環加載等)
require
返回的值是被輸出的值的拷貝,模塊內部的變化也不會影響這個值)。基本用法
//a.js
module.exports = function () {
console.log("hello world")
}
//b.js
var a = require('./a');
a();//"hello world"
//或者
//a2.js
exports.num = 1;
exports.obj = {xx: 2};
//b2.js
var a2 = require('./a2');
console.log(a2);//{ num: 1, obj: { xx: 2 } }
複製代碼
——摘抄 掘金 再次梳理AMD、CMD、CommonJS、ES6 Module的區別
異步加載,依賴前置,提早執行
//a.js
//define能夠傳入三個參數,分別是字符串-模塊名、數組-依賴模塊、函數-回調函數
define(function(){
return 1;
})
// b.js
//數組中聲明須要加載的模塊,能夠是模塊名、js文件路徑
require(['a'], function(a){
console.log(a);// 1
});複製代碼
異步加載,依賴就近,延遲執行
/** AMD寫法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) {
// 等於在最前面聲明並初始化了要用到的全部模塊
a.doSomething();
if (false) {
// 即使沒用到某個模塊 b,但 b 仍是提早執行了
b.doSomething()
}
});
/** CMD寫法 **/
define(function(require, exports, module) {
var a = require('./a'); //在須要時申明
a.doSomething();
if (false) {
var b = require('./b');
b.doSomething();
}
});
/** sea.js **/
// 定義模塊 math.js
define(function(require, exports, module) {
var $ = require('jquery.js');
var add = function(a,b){
return a+b;
}
exports.add = add;
});
// 加載模塊
seajs.use(['math.js'], function(math){
var sum = math.add(1+2);
});
複製代碼
——摘抄 掘金 前端模塊化:CommonJS,AMD,CMD,ES6
this
指向當前模塊,ES6 Module this
指向undefined
;——摘抄 掘金 再次梳理AMD、CMD、CommonJS、ES6 Module的區別
// a.js
const function a() => {
console.log("this is in a");
}
export {
a,
}
// b.js
import { a } from "./a";
a(); // this is in a
複製代碼
webpack 配置-output.libraryTarget
咱們但願包能夠在任何的環境下運行,支持常見的三種引用方式
因此輸出的libraryTarget要配置爲umd
libraryTarget: "umd"
- 將你的 library 暴露爲全部的模塊定義下均可運行的方式。它將在 CommonJS, AMD 環境下運行,或將模塊導出到 global 下的變量。瞭解更多請查看 UMD 倉庫。
webapck.config.json
var path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
library: "MyLibrary",
libraryTarget: "umd"
}
};複製代碼
最終輸出
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["MyLibrary"] = factory();
else
root["MyLibrary"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return _entry_return_; // 此模塊返回值,是入口 chunk 返回的值
});複製代碼
——摘自 webpack 配置-output.libraryTarget-模塊定義系統-umd
webpack 配置-libraryTargets【這些選項將致使 bundle 帶有更完整的模塊頭部,以確保與各類模塊系統的兼容性。根據 output.libraryTarget
選項不一樣,output.library
選項將具備不一樣的含義。】
webpack 配置-externals【防止將某些 import
的包(package)打包到 bundle 中,而是在運行時(runtime)再去從外部獲取這些
webpack 配置-targets【webpack能夠編譯成不一樣環境下運行的代碼,例如node、web(默認)】
安裝如下依賴,配置一個最基礎的webpack
npm install webpack webpack-cli -D複製代碼
webpack.condig.js
const path = require('path');
module.exports = {
mode:'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
library: "MyLibrary",
libraryTarget: "umd",
publicPath: "./",
}
};複製代碼
安裝如下依賴
npm install @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
npm install babel-loader -D 複製代碼
webpack編譯ES6的配置以下:
// ./webapck.config.js
var path = require('path');
module.exports = {
mode: process.env.NODE_ENV,
entry: { index: './src/index.js' },
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
library: "MyLibrary",
libraryTarget: "umd"
},
externals:
!process.env.debug
? ["react", "react-dom"]
: {
React: "react",
ReactDOM: "react-dom"
},
module: {
rules: [
{
test: /\.(jsx|js)$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/env"],
plugins: ["@babel/plugin-transform-runtime"]
}
},
],
include: [path.resolve(__dirname, "src")],
exclude: /(node_modules|bower_components)/,
}
]
}
};複製代碼
安裝如下依賴
npm install typescript
npm install ts-loader -D複製代碼
webpack編譯TS的配置以下,具體分析見下一篇
module: {
rules: [
{
test: /\.(tsx|ts)$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/env"],
plugins: ["@babel/plugin-transform-runtime"]
}
},
+ { loader: "ts-loader" }
],
include: [path.resolve(__dirname, "src")],
exclude: /(node_modules|bower_components)/,
}
]
},複製代碼
// tsconfig.json
{
"compilerOptions": {
"declaration": true, // 生成相應的 .d.ts文件。
"declarationDir": "./types", // 生成聲明文件的輸出路徑。
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
},
"allowSyntheticDefaultImports": true, // 容許從沒有設置默認導出的模塊中默認導入。這並不影響代碼的輸出,僅爲了類型檢查。
"experimentalDecorators": true, // 啓用實驗性的ES裝飾器。
"module": "ES6",
"target": "ES6",
"skipLibCheck": true, // 忽略全部的聲明文件( *.d.ts)的類型檢查。
"esModuleInterop": true, // 經過導入內容建立命名空間,實現CommonJS和ES模塊之間的互操做性
"moduleResolution": "node", // 決定如何處理模塊。或者是"Node"對於Node.js/io.js,或者是"Classic"(默認)。
"strict": true, // 啓用全部嚴格類型檢查選項。
"removeComments": false, // 刪除全部註釋,除了以 /!*開頭的版權信息。
"jsx": "react", // 在 .tsx文件裏支持JSX: "React"或 "Preserve"。
"sourceMap": true, // 生成相應的 .map文件。
"downlevelIteration": true // 當target爲"ES5"或"ES3"時,爲"for-of" "spread"和"destructuring"中的迭代器提供徹底支持
},
"exclude": ["node_modules", "build", "scripts", "**/*.css"] // 表示要排除的,不編譯的文件
}
複製代碼
安裝如下依賴
npm install @types/react @types/react-dom
npm install @babel/preset-react -D複製代碼
webpack編譯react的配置以下:
options: {
- presets: ["@babel/preset-env"],
+ presets: ["@babel/preset-env", "@babel/preset-react"],
plugins: ["@babel/plugin-transform-runtime"]
}複製代碼
要使用裝飾器的語法的話,須要安裝
npm install @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D複製代碼
plugins: [
// https://babeljs.io/docs/en/babel-plugin-proposal-decorators // If you are including your plugins manually and using @babel/plugin-proposal-class-properties, make sure that @babel/plugin-proposal-decorators comes before @babel/plugin-proposal-class-properties. // When using the legacy: true mode, @babel/plugin-proposal-class-properties must be used in loose mode to support the @babel/plugin-proposal-decorators. [ "@babel/plugin-proposal-decorators", { // Use the legacy (stage 1) decorators syntax and behavior. legacy: true } ], ["@babel/plugin-proposal-class-properties", { loose: true }],
"@babel/plugin-transform-runtime"]複製代碼
@babel/plugin-proposal-decorators
的legacy
設爲true
的話須要配置@babel/plugin-proposal-class-properties
的loose
爲true
, 詳見文檔
安裝如下依賴
npm install antd
npm install babel-plugin-import -D複製代碼
options: {
presets: ["@babel/env", "@babel/react"],
plugins: [
+ [
+ "import",
+ {
+ libraryName: "antd",
+ // libraryDirectory: "es", // 默認lib
+ style: true // `style: true` 會加載 less 文件
+ }
+],
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
"@babel/plugin-transform-runtime"
]
}複製代碼
對less文件作如下處理
安裝如下依賴
npm install less-loader css-loader style-loader -D複製代碼
{
test: /\.less$/,
use: [
{
loader:"style-loader"
},
{
loader: "css-loader",
options: {
modules: {
// localIdentName: '[path][name]__[local]',
getLocalIdent: (context, _, localName) => {
if (context.resourcePath.includes("node_modules")) {
return localName;
}
return `demo__${localName}`;
},
},
},
},
{
loader: "less-loader",
options: {
lessOptions: {
// http://lesscss.org/usage/#command-line-usage-options
javascriptEnabled: true,
modifyVars: {
// "primary-color": "#1DA57A",
// "link-color": "#1DA57A",
// "border-radius-base": "2px",
// or
// https://github.com/ant-design/ant-design/blob/d2214589391c8fc8646c3e8ef2c6aa86fcdd02a3/.antd-tools.config.js#L94
hack: `true; @import "${require.resolve( "./src/assets/style/ui.config.less" )}";` // Override with less file
}
}
}
}
]
},複製代碼
You can pass any Less specific options to the less-loader via loader options. See the Less documentation for all available options in dash-case.
——摘自 webpack less-loader
Global Variables
命令行寫法 |
json配置寫法 |
lessc --global-var="color1=red" |
{ globalVars: { color1: 'red' } } |
This option defines a variable that can be referenced by the file. Effectively the declaration is put at the top of your base Less file, meaning it can be used but it also can be overridden if this variable is defined in the file.
Modify Variables
命令行寫法 |
json配置 |
lessc --modify-var="color1=red" |
{ modifyVars: { color1: 'red' } } |
As opposed to the global variable option, this puts the declaration at the end of your base file, meaning it will override anything defined in your Less file.
——摘抄 less官方文檔