用typescript開發一個vue的第三方插件(一)

基於微前端qiankun寫一個vue專用的插件

本次項目的目的,是基於qiankun這個微前端框架寫一個vue專用的插件來方便在vue的項目裏使用。javascript

起初是想用js寫的。但本着如今開發的是一個第三方類庫,要考慮通用性。若是用js寫,若是使用的項目時typescript的vue項目,就會遇到缺乏類型定義文件的問題。因而採用ts開發。這樣申明文件的生成也變得容易一些。css

項目地址

https://github.com/Hades-li/qiankun-vuehtml

安裝

yarn add qiankun-vue
npm install qiankun-vue --save

從vue-cli到手寫webpack配置

vue-cli是vue官方的腳手架工具,傻瓜式構建項目,很是好用。但也許它更適合去開發一整套應用,而不是一個小小的第三方庫。前端

目錄結構

image.png

  • build: 存放webpack配置文件(後期手寫)
  • dist:最終文件輸出目錄
  • example:用於展現用的列子。其實這個目錄就是原來用vue-cli的生成的src目錄,但因爲咱們要開發一個第三方庫,src目錄是用來放庫源代碼的地方,因此就把目錄給換了。
  • src:用於放源代碼的地方。
  • types:typescript類型定義文件

用vue-cli官方構建方式(棄用)

vue-cli提供了一個官方的構建庫方法vue

vue-cli-service build --target lib --name myLib [entry]

這樣構建的結果就和官方展現的同樣,會同時生成多個js的庫文件。而實際在項目引用時,咱們只會用到一個。也就是說,咱們根本不但願生成一堆咱們不想要的東西。java

修改vue.config.js構建(棄用)

放棄使用vue-cli官方自帶的方法,剩下的方法就是改寫vue.config.js配置來快速實現構建。如下是截取一些重要的代碼段講解node

if (isProd) {
      config.entry('index').clear().add('./src/index.ts')
      config.output
        .filename('index.js') // 輸出文件名
        .libraryTarget('umd') // 打包類型
        .library('QiankunVue') // 全局變量名稱
        ......
}

在生產環境下,入口文件爲src路徑下的入口文件。
輸出文件設置成index.js,目標類型爲umd格式,全局變量爲QiankunVuejquery

if (!isProd) {
     config.entry('index').clear().add('./example/main.ts')
}

在開發環境下,目的不是把src的源代碼打包輸出,而是要運行example中的列子。因此就將入口文件設置成example下的main.ts文件。webpack

// 排除掉Vue
config.externals({
    vue: {
      root: 'Vue',
      commonjs: 'vue',
      commonjs2: 'vue',
      amd: 'vue'
    }
})

因爲咱們開發的是vue的插件,因此須要將vue排除掉。git

此時,執行yarn run build後,dist目錄中就會只生成一個index.js文件,固然,因爲我並無屏蔽掉html模板插件,index.html仍是會被生成。

最終我放棄了以上兩種vue-cli的構建方式,由於我發現,打包出來的index.js文件大小,高達120+KB。這個大小明顯偏大。但我卻找不到緣由。

webpack配置走起

此次改動,目的是作到徹底不依賴vue-cli,開發,生產環境徹底自定義。
image.png
build.js-採用函數式方式執行webpack打包。(暫時放棄)
如下是一些重點代碼片斷

webpack.config.base.js

基礎配置,包含了生產和開發環境都須要配置

module.rules

// 預處理.ts文件
{
  test: /\.ts$/,
  use: [
    'babel-loader',
    {
      loader: 'ts-loader',
      options: {
        appendTsSuffixTo: [/\.vue$/],
        transpileOnly: true
      }
    }
  ],
  exclude: /node_modules/
},
// 預處理.vue文件
{
  test: /\.vue$/,
  loader: 'vue-loader'
},

這兩段代碼主要是預處理.ts文件和.vue文件。
babel-loader是可選的,做用是將es6代碼轉成es5用於兼容瀏覽器。現在的chrome,edge,firefox等現代瀏覽器,對於es6甚至es7支持已經很好了。不加babel-loader,打包文件尺寸能進一步縮小,運行效率還能更高一些,但本着也許還有人用ie的態度,順手加一下吧。

// 預處理scss
    {
      test: /\.s[ac]ss$/,
      use: [
        env.NODE_ENV !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
        'css-loader',
        'sass-loader'
      ]
    },
    // 預處理css
    {
      test: /\.css$/,
      use: [
        env.NODE_ENV !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
        'css-loader'
      ]
    },

預處理scss,和css樣式。其實在vue-cli構建出來的項目中,還能支持less,stylu另外兩種預編譯樣式文件,但咱們如今是自定義webpack配置,本着,用到什麼就配什麼原則,只須要知足本身的需求便可。

// 預處理圖片
{
  test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
  loader: 'url-loader',
  options: {
    limit: 4096,
    esModule: false, // 5.0版本以上要加
    fallback: {
      loader: 'file-loader',
      options: {
        name: 'img/[name].[hash:8].[ext]'
      }
    }
  }
}

預處理圖片,這裏要着重說一下這個配置項的坑。此配置用於解析圖片/文件路徑,以及對圖片進行base64轉換。起初,我是直接用vue-cli審查出了一個webpack配置文件,將這部分拷貝過來,和以上代碼相比,僅僅是沒有esModule: false這個參數。運行的結果是,全部圖片不展現,查看圖片路徑是這樣<img src=[object module]>。因而加上esModule:false就ok了。

以上緣由是因爲vue-loader在對.vue文件解析時,

<img src="../image.png">

標籤會被編譯成

createElement('img', {
  attrs: {
    src: require('../image.png') // commonJS語法導入函數 如今這是一個模塊的請求了
  }
})

require是commonJS規範的導入函數。而url-loader默認是識別es6的導入語法,即import。因而,最終轉換出的代碼就沒法正常顯示圖片。esModule: false就是啓用commonJS引入方式。

那爲何vue-cli構建出來的項目則不須要配置這個參數?
由於vue-cli所採用的url-loader版本號還停留在2.x.x。而最新版本已經到了4.x.x。舊版本url-loader是沒有這個限制機制。新版url-loader是鼓勵採用es6的標準規範來引入文件。commonJs是nodejs的規範。

plugins

plugins: [
      new VueLoaderPlugin(), // 配合vue-loader
      new ForkTsCheckerWebpackPlugin(), // 將ts-loader類型檢查跑在一個獨立線程加速編譯
      new webpack.DefinePlugin({
        'process.env': {
          NODE_ENV: '"' + env.NODE_ENV + '"',
          BASE_URL: '"/"'
        }
      }), // 給瀏覽器代碼中添加全局變量
      new FriendlyErrorsPlugin(), // 友好的錯誤提示
      new MiniCssExtractPlugin(), // css提取
      new CaseSensitivePathsPlugin() // 嚴格路徑大小寫
    ]

以上這些插件基本都是看着vue-cli審查加入的。有的能夠不加,但加了,對於開發都是有極大幫助的。

webpack.config.prd.js

生產環境下的配置。

module.exports = merge(baseConf({ NODE_ENV: env }), {
  mode: env,
  entry: './src/index.ts',
  output: {
    path: resolve('dist'),
    filename: 'index.js',
    publicPath: '/',
    chunkFilename: 'js/[name].[contenthash:8].js',
    libraryTarget: 'umd',
    library: 'QiankunVue'
  },
  externals: {
    vue: {
      root: 'Vue',
      commonjs: 'vue',
      commonjs2: 'vue',
      amd: 'vue'
    }
  },
  plugins: [
    // 清理dist文件夾
    new CleanWebpackPlugin()
  ]
})

生產環境配置較爲簡單,首先經過webpack-merge這個插件將webpack.config.base.js的配置合併過來,添加entry,和output的配置項,而且用cleanwebpackPlugin插件清理一下dist目錄,就能夠一鍵打包了。

webpack.config.dev.js

開發環境下的配置。

entry: './example/main.ts',
  output: {
    path: resolve('dist'),
    filename: 'app.js',
    publicPath: '/'
  },
  devServer: {
    contentBase: resolve('dist'),
    port: 8080,
    hot: true,
    progress: true, // 0-100%的進度提示
    quiet: true // 去掉一堆告警信息
  },
  devtool: 'eval-source-map',

開發環境,一樣合併webpack.config.base.js,改寫一下entry指向example/main.ts。
配置一下開發服務器devServer,切記,別忘了安裝webpack-dev-server這個包。

plugins

plugins: [
    new webpack.HotModuleReplacementPlugin(), // 支持熱模塊替換
    new HtmlWebpackPlugin({
      title: 'qiankun',
      template: resolve('public/index.html'),
      favicon: resolve('public/favicon.ico')
    }), // 配置index.html模板
    new CopyWebpackPlugin([
      {
        from: resolve('public'),
        to: resolve('dist'),
        toType: 'dir',
        ignore: ['index.html']
      }
    ]) // 拷貝插件,用於拷貝一些不參與打包的靜態資源至dist目錄
  ]

這些都是開發模式下必要的插件

以上的這些webpack配置,比起vue-cli提供的要少不少,好比咱們甚至都沒有針對.js文件編譯loader配置項,由於咱們的項目中純玩ts。實際用vue-cli構建的ts項目是能夠進行js,ts混合開發的。但純手寫webpack的目的,就是隻配本身須要的。

本期重點介紹webpack配置,這也是此項目裏難點之一,耗時費力。

遺留問題1-沒法自動生成*.d.ts文件

開發一個標準的js庫,無論你是用js開發仍是ts,現在,都是要給本身的庫寫ts的類型申明文件。像相似lodash,jquery這些著名的庫,早期都是沒有申明文件。致使若是用於ts項目開發,類型檢測機制就沒法進行(早期沒有typescript)。但如今,這些庫都已經將類型申明補充進來了。
類型申明能夠手寫,但太費事。最好固然是自動生成。
按照官方說法,在tsconfig.js中配置了declaration: true的屬性時,就能夠自動生成。但實際是沒有任何反應。固然,你若是直接用tsc --declaration命令行編譯,則能夠生成,目前這是個遺留問題。若是有大神知道解決方法忘指教。

相關文章
相關標籤/搜索