猛虎下山

东药王庙旧址

离家2019

入职某知名在线青少年英语教育机构十几天了,在长达四个月的潜伏期之后,小公司终于歇业了。我们几个老人不得不出来打工赚钱,再次加入这个节奏的开发环境,一时间我精神高度紧张,初期投入再次刷爆了信用卡。由于新东家有住房补助的原因,租了在北京这么多年最贵的房子,我也算在二环安顿下来了。

Jack即将远行到深圳去工作,这样一来,几位创始人就都背井离乡了,留下仨孩子妈妈在家拉扯娃们,心中虽有各种不忍,也算强行脱离安全区了.
在家蛰伏期间,困顿无比,总觉得奔四年纪的我在家无所事事,甚至接盘了儿子安保教育长达一个半月,期间岳母、夫人先后入院折腾,现在回想起来,在家人需要的时候,我正好在家,未尝不是一种幸运啊。

这几日跟夫人和孩子视频的时候,我强烈想家,我在我自己的“豪宅”里盘桓了那么久,习惯了躺在沙发上跟儿子玩Switch可以抡圆了打ARMS,昏睡的时候,也不会被楼上练古筝的逼的心率加速,心爱的书籍和设备也都在我希望的地方好好安放着,最爱的人每天都能在一起,就算儿子扎刺儿了我也能直接收拾到他啊。

我没有写2018年总结,就是因为那个困顿的时间,我难受非常,不咖也不想去,总觉得没有可以用来牛逼的资本。而离开家后,在经济寒冬中再次逆袭京城,面临的挑战之多,让不得不心生廉颇老矣之叹。夫人常说,我是满腹才学,完全没有必要如此不自信。可是我还是对自己年龄介怀于心。

下面开始记录这一周多以来解决的坑

Background

I need to create a pure Javascript package for uploading file to server. Let’s call it “js-uploader”.

Then I will use it in a Vue Component, with baisc UI so the backend server management system could import it and upload media files, I’ve named it “vue-uploader”.

Finally, I should use both js-uploader and vue-uploader in the media management system page. It’s the “mms”.

Create NPM Package

I created js-uploader with typical setting by yarn, yarn is a more preferable choice for me than npm. I felt it’s running faster than npm(Or it’s just kind of illusion).

I like it running in the up-to-date way in ES6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const axios = require('axios');
const BMF = require("browser-md5-file");
const CancelToken = axios.CancelToken;
module.exports = {
/**
* @description 获取运行时环境是否为Node
* @returns {Boolean} - 运行在Node环境中时为 true
*/
isNode() {
return typeof window === 'undefined';
},
/**
* @description 根据不同的运行时环境返回对应的表单Class
* @returns {Function}
*/
getFormDataClass() {
if (this.isNode()) {
const FormData = require('form-data');
return FormData
}
return FormData;
}
...
}

Use in Vue component

Well, It works right after I practically publish it to npmjs, and install it in vue-uploader.

  • In js-uploader
    1
    yarn publish
  • In vue-uploader
    1
    yarn add js-uploader
  • Then in vue-uploader App.vue
    1
    2
    3
    4
    5
    import * as v_up from "js-uploader";
    //try it out in mounted
    mounted(){
    console.log(v_up);
    }
    And I got this output:
    cjs-export-output

But the problem came after that is , I can’t publish it everytime I made a nuance change of the code. As we know npm provides build-in solution to develop locally called ‘npm link’.
So what I did is:

  • In js-uploader
    1
    2
    3
    4
    yarn link 
    success Registered "js-uploader".
    info You can now run `yarn link "js-uploader"` in the projects where you want to use this package and it will be used instead.
    ✨ Done in 0.05s.
  • In vue-uploader
    1
    2
    3
    4
    5
    yarn link js-uploader 
    yarn link v1.13.0
    warning package.json: No license field
    success Using linked package for "js-uploader".
    ✨ Done in 0.06s.

Serve vue-uploader after this,you will receive warning like this:

1
"export 'isNode' (imported as 'v_up') was not found in 'js-uploader'

Ok,just warning, doesn’t matter.
But in the browser, you’ll get error like this:

1
Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

Seems I encountered a classic Exports Problem

Export in CommonJS vs ES6

According to the blog or any other introduction online. CommonJS works in Node env while ES6 works in modern Javascript like In the Browser.

Export and Import makes your code can be reused in different parts of your app. Moreover, they can be shared to other developers.

  • exports and imports in CommonJS
    1
    2
    3
    4
    5
    6
    // file cjs.js
    module.exports.hiThere = function(){
    return "Hi, there"
    }
    const cjs = require('cjs.js')
    cjs.hiThere()
  • exports and imports in ES6
    1
    2
    3
    4
    5
    6
    // file es6.js
    export function hiThere(){
    return "Hi, there"
    }
    import { hiThere } from "es6"
    hiThere()
    I took the CommonJS form as the way to code, cause it’s more like a npm pack than a frontend package.
    It won’t work until you give a way to convert it from CommonJS to ES6.

One of the solution is to install “@babel/plugin-transform-runtime” and use it as the plugin in vue project’s babel.config.js file

1
2
3
4
5
6
module.exports = {
presets: [
'@vue/app'
],
"plugins": ["@babel/plugin-transform-runtime","@babel/plugin-transform-modules-commonjs"]
}

It works!

Export Vue Component

I want to publish the vue-uploader as a component. These are the steps:

  1. create your vue component like this:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <template>
    <el-dialog
    title="Upload Preview"
    :visible.sync="show"
    width="50%"
    :before-close="handleClose"
    :close-on-click-modal="false"
    top="5vh">
    <el-col :span="24">
    <VKUploader type="video"/>
    <VKUploader type="image"/>
    </el-col>
    <span slot="footer" class="dialog-footer">
    <el-button type="primary">保存</el-button>
    <el-button @click="handleClose">取 消</el-button>
    </span>
    </el-dialog>
    </template>
    <script>
    import VKUploader from './VKUploader'
    export default {
    name:"VKUploaderDialog",
    components:{
    VKUploader
    },
    data(){
    return {
    }
    },
    props:{
    show:Boolean
    },
    methods:{
    handleClose(){
    this.$emit('close');
    }
    }
    }
    </script>
  2. create a index.js file in folder ‘components’. Import and register the component you need in it:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import Vue from 'vue'
    import VKUploader from "./VKUploader"
    import VKUploaderDialog from "./VKUploaderDialog"


    const Components = {
    VKUploader,
    VKUploaderDialog
    }
    Object.keys(Components).forEach(name =>{
    Vue.component(name, Components[name]);
    })
    export default Components;

    Notice that here’s using ES6 way to import, Be versatile!

  3. The paramount part, use vue-cli tool to build the package. Add a script in package.json file:

    1
    2
    3
    4
    5
    6
    "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "build-bundle": "vue-cli-service build --target lib --name vue-uploader ./src/components/index.js",
    }

    Run it, you’ll get production js file in dist folder.

  4. Change main to the dist in package.json.

    1
    "main": "./dist/vue-uploader.common.js",

    Now you can publish or use it in other Vue project like pro.

Local Debug in MMS

Install vue-uploader from online npm is OK. But if you try to link it like before, It won’t work as you expected. Here are the steps to setup:

  1. install the package through local path:

    1
    yarn add "the-path-to-vue-uploader"
  2. import it in main.js so the components can be register. Don’t forget the css file.

    1
    2
    import "vue-uploader"
    import "vue-uploader/dist/vue-uploader.css"
  3. You’re all set.

JSDoc

BTW, I used basic jsdoc3 to generate docs for the javascript package.

1
2
yarn add jsdoc
yarn add ink-docstrap #the theme

npm script

1
"jsdoc": "./node_modules/.bin/jsdoc src/** -d ./docs/ -c ./jsdoc.json -t ./node_modules/ink-docstrap/template"