Android开发学习

打包神器之Parcel使用指南

2018-06-27  本文已影响0人  小明小明长大了

简介

Blazing fast, zero configuration web application bundler.官网 github

快速,零配置的 Web 应用程序打包器。

主要有以下特点:

其他打包工具对比

快速使用

  1. 下载全局依赖
npm install -g parcel-bundler
  1. 新建html,css,js文件
// index.html

<html>
  <body>
    <p>hello world</p>
    <script src="./index.js"></script>
  </body>
</html>

// index.js

import './index.css';
alert(1);

// index.css

body {
  background: red;
}
  1. 本地开发
parcel serve index.html
  1. 打包应用
parcel build index.html 

命令行选项介绍

  1. parcel主要命令
parcel -h # 查看帮助信息
parcel serve -h # 本地开发模式,会启动一个默认1234端口的本地服务器,代理到配置的目标文件,前端页面开发为主
parcel watch -h # 监听文件改动模式,不会启动默认本地服务器,服务端开发为主
parcel build -h # 编译源文件并打包到目标�文件夹
  1. parcel重要选项介绍
  -p, --port <port> # �本地服务器启动端口
  --open # 是否打开默认浏览器
  --public-url # 静态资源文件夹路径
  --no-hmr # false 是否开启代码热更新
  --no-cache  # false 是否启用缓存机制,用于缓存引起的代码更新不及时的问题排查
  --no-source-maps # false 是否启用source-maps文件,主要用于错误排查与定位
  --no-autoinstall # false 很多资源文件的loader包是自动下载的,代码会将没有路径的包当成npm包自动下载,发现代码自动下载不需要的npm包是,需要开启这个属性
  -t, --target [target] # browser 代码目标环境 node | browser | electron
  --log-level <level> # 1 日志输入级别 0 | 1 | 2 | 3 对应 不输出日志 | 仅错误日志 | 警告与错误日志 | 所有日志 
-d, --out-dir <path> # 打包目标文件夹 默认 "dist"
-o, --out-file <filename> # 项目入口文件的文件名,默认与 --public-url 一致
--no-minify # 打包时不压缩源文件
--detailed-report # 打印详细打包资源报告

主要使用场景实例--- 所有demo无需手动写依赖与配置

由于parcel可以自动加载 vue,react...等框架的依赖包,所以不需要自己特地去将依赖加到package.json中,直接写vue,react组件即可。

vue demo

  1. 编写src/app.vue
<template lang="html">
    <div id="app">
      <h1>Hello Parcel vue app 📦 🚀</h1>
    </div>
  </template>
  
<script>
    export default {
        name: 'app'
    }
</script>

<style lang="css">
    #app {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100%;
    }
    h1 {
        font-weight: 300;
    }
</style>
  1. 编写src/main.js
import Vue from 'vue'
import App from './app.vue'

new Vue({
  el: '#app',
  render: h => h(App)
});
  1. 编写index.html
<html>
<head>
    <title>Welcome to Vue</title>
</head>
<body>
    <div id="app"></div>
    <script src="src/main.js"></script>
</body>
</html>
  1. 运行与打包
# 运行
parcel serve pathto/index.html --no-cache
# 打包
parcel build pathto/index.html --public-url . --no-source-maps --no-cache --detailed-report

react-typescript demo

  1. 编写components/App.tsx组件
import * as React from 'react'

export default class App extends React.Component<any, any> {
    render() {
        return (
            <div className="container">
                <h1>typescript react component</h1>
            </div>
        )
    }
}
  1. 编写index.tsx入口
import * as React from 'react'
import { render } from 'react-dom'

import App from './components/App'

render(<App />, document.getElementById('root'))
  1. 编写index.html
<html lang="en">
<head>
  <title>Parcel with Typescript</title>
</head>
<body>
  <div id="root"></div>
  <script src="./index.tsx"></script>
</body>
</html>
  1. 开发与打包
parcel serve pathto/index.html --no-cache
# 打包
parcel build pathto/index.html --public-url . --no-source-maps --no-cache --detailed-report

多页面应用

  1. 建立pages文件夹放html文件
// index.html
<html lang="en">
<head>
    <title>index</title>
</head>
<body>
    <nav>
        <ul>
            <li><a href="./page1.html">第一页</a></li>
            <li><a href="./page2.html">第二页</a></li>
            <li><a href="./page3.html">第三页</a></li>
        </ul>
    </nav>
    <h1>这是首页</h1>
</body>
</html>

// page1.html
<html lang="en">
<head>
    <title>Page 1</title>
</head>
<body>
    <h1>第一页</h1>
    <a href="./index.html">返回首页</a>
    <script src="../js/page1.js"></script>
</body>
</html>

// page2.html
<html lang="en">
<head>
    <title>Page 2</title>
</head>
<body>
    <h1>第二页</h1>
    <a href="./index.html">返回首页</a>
    <script src="../js/page2.js"></script>
</body>
</html>

// page3.html
<html lang="en">
<head>
    <title>Page 3</title>
</head>
<body>
    <h1>第三页</h1>
    <a href="./index.html">返回首页</a>
    <script src="../js/page3.js"></script>
</body>
</html>
  1. 建立css文件夹放less文件
// base.less
body {
    background: grey;
    color: #ffffff;
}

// page1.less
body {
    background: red !important;
}

// page2.less
body {
    background: black !important;
}

// page3.less
body {
    background: green !important;
}
  1. 建立js文件夹放js文件
// base.js
import '../css/base.less';

export const baseFunc = (text) => {
    alert(`baseFunc --- by ${text}`);
}

// page1.js
import '../css/page1.less'
import { baseFunc } from './base'

baseFunc('page1');

// page2.js
import '../css/page2.less'
import { baseFunc } from './base'

baseFunc('page2');

// page3.js
import '../css/page3.less'
import { baseFunc } from './base'

baseFunc('page3');
  1. 开发与打包 注意这里使用 * 号匹配html路径
# 开发 
parcel serve pathto/pages/*.html --no-cache
# 打包
parcel build pathto/pages/*.html --public-url ./ --no-source-maps --no-cache --detailed-report

parcel原理简介

parcel flow.jpg

写一个parcel 识别 **.json2文件parcel 插件

  1. 写一个 Asset 实现类 myAsset.js
const path = require('path');
const json5 = require('json5');
const {minify} = require('terser');
const {Asset} = require('parcel-bundler');

class MyAsset extends Asset {
  constructor(name, options) {
    super(name, options);
    this.type = 'js'; // set the main output type.
  }

  async parse(code) {
    // parse code to an AST
    return path.extname(this.name) === '.json5' ? json5.parse(code) : null;
  }

  // async pretransform() { // 转换前
  //   // optional. transform prior to collecting dependencies.
  // }

  // collectDependencies() { // 分析依赖
  //   // analyze dependencies
  // }

  // async transform() { // 转换
  //   // optional. transform after collecting dependencies.
  // }

  async generate() { // 生成代码
    // code generate. you can return multiple renditions if needed.
    // results are passed to the appropriate packagers to generate final bundles.
    let code = `module.exports = ${
        this.ast ? JSON.stringify(this.ast, null, 2) : this.contents
    };`;
  
    if (this.options.minify && !this.options.scopeHoist) {
      let minified = minify(code);
      if (minified.error) {
          throw minified.error;
      }
      code = minified.code;
    }

    return [{
      type: 'json2',
      value: this.contents
    }, {
      type: 'js',
      value: code
    }];
  }

  // async postProcess(generated) { // 生成代码完成之后操作
  //   // Process after all code generating has been done
  //   // Can be used for combining multiple asset types
  // }
}

module.exports = MyAsset;
  1. 写一个 Packager 实现类 myPackager.js
const {Packager} = require('parcel-bundler');

class MyPackager extends Packager {
  async start() { // 文件头之前的内容
    // optional. write file header if needed.
    await this.dest.write(`\n123-before\n`);
  }

  async addAsset(asset) { // 文件内容
    // required. write the asset to the output file.
    await this.dest.write(`\n${asset.generated.json2}\n`);
  }

  async end() { // 写在文件尾 的内容
    // optional. write file trailer if needed.
    await this.dest.end(`\nabc-after\n`);
  }
}

module.exports = MyPackager;
  1. 编写插件方法myPlugin.js
module.exports = function (bundler) {
    bundler.addAssetType('.josn2', require.resolve('./MyAsset'));
    bundler.addPackager('json2', require.resolve('./MyPackager'));
};
  1. 发布到npm中
    将这个包发不到npm时,需要加parcel-plugin-前缀,然后它就会被parcel自动识别。
  2. 使用插件两种方式
const path = require('path');
const Bundler = require('parcel-bundler');
const bundler = new Bundler(file, options);

// 获取node命令行的参数
const args = process.argv.splice(2);

// Entrypoint file location
const file = path.join(__dirname, './src/index.html');
// Bundler options
const options = {
  outDir: './demo_custom/dist', // The out directory to put the build files in, defaults to dist
  //   outFile: './demo_custom/dist/index.html', // The name of the outputFile
  //   publicUrl: './demo_custom/dist', // The url to server on, defaults to dist
  watch: true, // whether to watch the files and rebuild them on change, defaults to process.env.NODE_ENV !== 'production'
  cache: false, // Enabled or disables caching, defaults to true
  cacheDir: '.cache', // The directory cache gets put in, defaults to .cache
  minify: true, // Minify files, enabled if process.env.NODE_ENV === 'production'
  target: 'browser', // browser/node/electron, defaults to browser
  https: false, // Serve files over https or http, defaults to false
  logLevel: 3, // 3 = log everything, 2 = log warnings & errors, 1 = log errors
  hmrPort: 0, // The port the HMR socket runs on, defaults to a random free port (0 in node.js resolves to a random free port)
  sourceMaps: args[0] !== 'build', // Enable or disable sourcemaps, defaults to enabled (not supported in minified builds yet)
  hmrHostname: '', // A hostname for hot module reload, default to ''
  detailedReport: args[0] === 'build', // Prints a detailed report of the bundles, assets, filesizes and times, defaults to false, reports are only printed if watch is disabled
  open: true,
  port: 1234,
  production: args[0] === 'build'
};

const runBundle = async () => {
  // Initializes a bundler using the entrypoint location and options provided
  const bundler = new Bundler(file, options);
  bundler.addAssetType('.json2', require.resolve('./myAsset')); // 引入刚刚写好的资源识别类 【识别xx.json2类型文件】
  bundler.addPackager('json2', require.resolve('./myPackager')); // 引入刚刚写好的打包类【打包 xx.json2 类型文件】
  if (cli === 'serve' && options.open) {
    const server = await bundler.serve(options.port);
    if (server) {
        await require('parcel-bundler/src/utils/openInBrowser')(`http://localhost:${options.port}`, true)
    }
  } else {
    childProcess.exec(`rm -rf ${path.join(__dirname, './dist')}`);
    bundler.bundle();
  }
};

parcel实践应用show-pages

该项目简单二次应用了parcel 工具,使用了postcss,posthtml,以及一些模版替换的,纯前端项目,已可以实现基本功能。该项目现已稍微搁置,没有进步继续编写。后边有时间,会继续完善这个项目,代码仅供参考。非常希望有小伙伴与我一起该贡献

总结

番外篇【源码解读,待更新...】


引用博文及项目链接

本文仅是自己个人观点,如果纰漏与错误,欢迎评论指正

上一篇下一篇

猜你喜欢

热点阅读