在 APICloud 項目中使用 Webpack

前言

最近項目所需,因此開始學習而且使用 APICloud 此款 hybrid APP 開發框架。粗略的看了下文檔和部分 Demo 後,已經對 APICloud 開發有必定基礎的瞭解。在這種過程令我有一點疑惑,APICloud 的開發流程和普通的 Web APP 開發實際上是很類似的,可是卻沒有對目前主流的構建工具備先關的教程。我發現雖然官方提供了一個 apicloud-polyfill 的功能,可是隻是爲了解決在項目中使用 ES6 的問題,並且整個 demo 的開發體驗並很差,每次修改代碼須要不停的構建在增量更新。隨着項目的複雜起來,以及對一些前端新特性的依賴,因此我打算本身動手弄一個適合 APICloud 使用的 Webpack 模板。javascript

項目結構

APICloud 的開發模式其實就是之前的使用 jQuery 的 MPA 開發同樣,並且官方不推薦使用 SPA 的開發模式。css

|- dist
|- lanuch
|- src
    |- pages
        |- Foo
            |- index.js
            |- index.html
    |- image
    |- css
    |- scripts
    |- index.js
    |- index.html
|- package.json
|- config.xml
|- webpack.config.json
|- .babelrc
|- .gitignore
|- .syncignore
|- index.html

上面是我我的對 APICloud 項目的文件夾分類。全部的源碼都存放在 src 文件夾內,Webpack 打包的主要目錄也是 src。html

這裏規定了每個單獨的 HTML 頁面都是須要一個 JavaScript 的入口文件,這也是爲了使用 Webpack 進行多頁面打包的基礎。前端

Webapck 基礎配置文件

在設定好目錄結構以後,咱們須要開始編寫 Webpack 的 config 文件,配置相對來首都比較簡單,因此咱們就寫在一個文件內既可。vue

webpack.config.jsjava

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin')
const glob = require('glob')

const paths = {}
const configs = []
const setPath = (item, type) => {
  const dirname = path.dirname(item)
  
  if (paths[dirname]) {
    paths[dirname][type] = item
  } else {
    paths[dirname] = {
      [type]: item
    }
  }
}

glob.sync('./src/**/*.js', { ignore: './src/script/*.js' }).forEach(item => setPath(item, 'script'))
glob.sync('./src/**/*.html').forEach(item => setPath(item, 'html'))

Object.keys(paths).forEach(key => {
  const distName = key.replace('src', 'dist')

  configs.push({
    entry: paths[key].script,
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, distName)
    },
    devtool: process.env.NODE_ENV === 'production' ? 'inline-source-map' : '#source-map',
    devServer: {
      host: '0.0.0.0',
      port: 8080,
      historyApiFallback: false,
      noInfo: true
    },
    resolve: {
      extensions: ['.js', '.vue'],
      alias: {
        '@': path.resolve(__dirname, 'src'),
        'vue$': 'vue/dist/vue.esm.js'
      }
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /(node_modules|bower_components)/,
          loader: 'babel-loader'
        },
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|svg|jpg|gif)$/,
          use: [
            'file-loader'
          ]
        },
        {
          test: /\.(woff|woff2|eot|ttf|otf)$/,
          use: [
            'file-loader'
          ]
        }
      ]
    },
    plugins: [].concat(
      [
        new HtmlWebpackPlugin({
          inlineSource: '.(js|css)$',
          template: paths[key].html
        })
      ],
      process.env.NODE_ENV === 'production' ? [
        new webpack.DefinePlugin({
          'process.env': {
            NODE_ENV: '"production"'
          }
        }),
        new webpack.optimize.UglifyJsPlugin({
          sourceMap: true,
          compress: {
            warnings: false
          }
        }),
        new webpack.LoaderOptionsPlugin({
          minimize: true
        }),
        new HtmlWebpackInlineSourcePlugin(),
      ] : []
    )
  })
})

module.exports = configs

package.jsonnode

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --progress ",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.25.0",
    "babel-loader": "^7.1.1",
    "babel-preset-env": "^1.6.0",
    "cross-env": "^5.0.5",
    "css-loader": "^0.28.4",
    "file-loader": "^0.11.2",
    "glob": "^7.1.2",
    "html-webpack-inline-source-plugin": "0.0.9",
    "html-webpack-plugin": "^2.30.1",
    "style-loader": "^0.18.2",
    "webpack": "^3.5.4",
    "webpack-dev-server": "^2.7.1"
  },
  "dependencies": {
    "vue": "^2.4.2"
  }
}

在 Webpack 中咱們導出的是一個 config 數組,經過掃描 src 文件下的 JavaScript 和 HTML 文件,獲取它們的 path 信息,爲每個 JavaScript 進行打包。此外咱們可能還須要忽略一些 JavaScript 文件。webpack

在生產環境的構建下,我配置了將 css 和 JavaScript 都插入了 html 文件中,這又是官方的一種提倡作法。git

估計上面的配置好後,設置兩個 npm script 命令,方便開發使用:github

  • npm run dev:啓動本地開發服務器;
  • npm run build:編譯項目,輸出到 dist 目錄下。

修改入口地址

在 APICloud 項目裏,咱們在根目錄有一個 index.html 文件,這是一個入口文件,咱們能夠經過修改 config.xml 來修改入口文件。通常來講都是經過 openFrame 或者 openWin 來嵌入或者打開一個頁面,在咱們的本地開發環境下,咱們經過 Webpack 啓動了一個本地服務器進行調試,因此咱們也須要將地址從目錄的相對地址改成一段 url。例如:

<script type="text/javascript">
  apiready = function () {
    api.openFrame({
      name: 'main',
      // url: 'dist/index.html',
      url: 'http://<你的本機 ip>:[端口號]',
      rect: {
        x: 0,
        y: 45,
        w: 'auto',
        h: 'auto'
      }
    })
  }
</script>

若是須要編譯項目的話,入口文件也須要修改爲相對路徑。這是一個麻煩的點,目前還沒什麼好辦法解決。

集成 ESlint

目標,在開發和編譯過程當中 Webpack 可以使用 eslint 檢查代碼,出現錯誤不經過,使用 aribnb 的 JavaScript 風格編程。

webpack.config.json

...
configs.push({
    ...
    module: {
        rules: [
            {
              enforce: "pre",
              test: /\.js$/,
              exclude: /(node_modules|bower_components)/,
              loader: "eslint-loader",
            },
            ...
        ]
    }
})
...

package.json

{
    ...
    devDependencies: {
        ...
        "eslint": "^4.4.1",
        "eslint-config-airbnb-base": "^11.3.1",
        "eslint-loader": "^1.9.0",
        "eslint-plugin-html": "^3.2.0",
        "eslint-plugin-import": "^2.7.0",
        ...
    }
    ...
}

.eslintrc

{
  "extends": "airbnb-base",
  "plugins": [
    "html"
  ]
}

.eslintignore

node_modules
dist

*.css

Airbnb JavaScript Style Guide

結語

此次對 APICloud 集成 Webpack 嘗試,結果還算可。可是我的對 APICloud 的開發仍是很是的不滿意的,對於 React Native 來講:沒有熱更新、在 APP loader 下調試不能使用 Chrome developer tool等。看網上對這個框架的評價,也是處於一個較低的水平。只能經過此次項目的開發再深刻了解的它們的利弊了。

項目地址:APICloud-Webpack-Demo

相關文章
相關標籤/搜索