linshuai

linshuai 查看完整档案

东莞编辑东莞理工学院  |  计算机科学与技术 编辑undefined  |  前端工程师 编辑 lin-xin.gitee.io/ 编辑
编辑

专注前端,文章首发于博客:https://lin-xin.gitee.io/

个人动态

linshuai 收藏了文章 · 10月26日

1024 程序员必备好物推荐 丨我和我的“格子衫”

前言

今天正好是 10月24日,每年一度的程序员节日。因为1024是2的十次方,二进制计数的基本计量单位之一。针对程序员经常周末加班与工作日熬夜的情况,部分互联网机构倡议每年的10月24日为1024程序员节,在这一天建议程序员拒绝加班。(然而我正在加班中。。。)

既然是节日,那在这我提前给大家拜个早年,祝大家头发多多,身体棒棒,bug少少。

好物推荐

回到主题,推荐程序员必备好物。

保温杯

image

某宝或某多多上,9块9包邮上楼的玻璃保温杯,必须带隔网的。

  • 程序员每天都坐在座位上对着电脑,缺少运动。而这款杯子,容量大约200-300ml,喝几口就喝完了,这时就需要起身走一走,去倒杯水,运动一下。
  • 带隔网呢,就是需要泡点什么了,像我平时就会泡几片西洋参,几朵小菊花,几颗大枸杞,几斤铁观音。
  • 外观方面呢,没什么特别,适合男女老少,四季通用。

对象

image

这里说的对象是真的象,一对象的桌面摆件。

  • 作为程序员,我们一般都是要面向对象编程。面向对象编程(Object Oriented Programming,OOP)是一种计算机编程架构。OOP的一条基本原则是计算机程序由单个能够起到子程序作用的单元或对象组合而成。OOP达到了软件工程的三个主要目标:重用性、灵活性和扩展性。
  • 把对象摆在电脑前,让程序员真正的面向对象编程,bug也可以少一些
  • 这个价格也小几十块,有点贵,超过9块9我就买不起,但我富裕的同事已经在桌面摆上了

image

这里说的书,也是真的书,只不过他的作用已经变了,不再是知识的提供者,而是颈椎的保护者。

  • 每天与我们最亲近的就是显示器,显示器过低的话,会导致我们一直低着头,对颈椎不好。而建议显示器的最佳的角度是目光与显示器平面相互垂直,垂足位于屏幕中心。
  • 而书,特别是不看的书,放在桌面占地方吃灰,还不如用来垫显示器,既不占地方,又能发挥它多余的作用,还能帮助我们的颈椎,何乐而不为。
  • 书的话就不用特地去买了,收集一下同事或自己不看的书就可以。
本文参与了 SegmentFault思否征文「1024 征文活动」,欢迎正在阅读的你也加入。
查看原文

linshuai 发布了文章 · 10月24日

1024 程序员必备好物推荐 丨我和我的“格子衫”

前言

今天正好是 10月24日,每年一度的程序员节日。因为1024是2的十次方,二进制计数的基本计量单位之一。针对程序员经常周末加班与工作日熬夜的情况,部分互联网机构倡议每年的10月24日为1024程序员节,在这一天建议程序员拒绝加班。(然而我正在加班中。。。)

既然是节日,那在这我提前给大家拜个早年,祝大家头发多多,身体棒棒,bug少少。

好物推荐

回到主题,推荐程序员必备好物。

保温杯

image

某宝或某多多上,9块9包邮上楼的玻璃保温杯,必须带隔网的。

  • 程序员每天都坐在座位上对着电脑,缺少运动。而这款杯子,容量大约200-300ml,喝几口就喝完了,这时就需要起身走一走,去倒杯水,运动一下。
  • 带隔网呢,就是需要泡点什么了,像我平时就会泡几片西洋参,几朵小菊花,几颗大枸杞,几斤铁观音。
  • 外观方面呢,没什么特别,适合男女老少,四季通用。

对象

image

这里说的对象是真的象,一对象的桌面摆件。

  • 作为程序员,我们一般都是要面向对象编程。面向对象编程(Object Oriented Programming,OOP)是一种计算机编程架构。OOP的一条基本原则是计算机程序由单个能够起到子程序作用的单元或对象组合而成。OOP达到了软件工程的三个主要目标:重用性、灵活性和扩展性。
  • 把对象摆在电脑前,让程序员真正的面向对象编程,bug也可以少一些
  • 这个价格也小几十块,有点贵,超过9块9我就买不起,但我富裕的同事已经在桌面摆上了

image

这里说的书,也是真的书,只不过他的作用已经变了,不再是知识的提供者,而是颈椎的保护者。

  • 每天与我们最亲近的就是显示器,显示器过低的话,会导致我们一直低着头,对颈椎不好。而建议显示器的最佳的角度是目光与显示器平面相互垂直,垂足位于屏幕中心。
  • 而书,特别是不看的书,放在桌面占地方吃灰,还不如用来垫显示器,既不占地方,又能发挥它多余的作用,还能帮助我们的颈椎,何乐而不为。
  • 书的话就不用特地去买了,收集一下同事或自己不看的书就可以。
本文参与了 SegmentFault思否征文「1024 征文活动」,欢迎正在阅读的你也加入。
查看原文

赞 4 收藏 1 评论 0

linshuai 收藏了文章 · 10月16日

VS Code插件开发介绍(一)

  • 前言

前段时间做了一个基于命令行的效率工具,可以自动生成组件的模板代码。自己用起来还觉得挺好,但在组内案例几次后大家都不愿意用,究其原因还是命令行工具使用起来门槛有点高,不方便。由于组内已经统一使用VS Code进行开发了,于是决定研究下VS Code的插件开发,让效率工具更方便的用起来。

  • 准备工作

为了降低开发门槛,微软做了一个Yeoman代码生成命令,可以很方便的生成插件开发需要的模板代码,可以通过以下命令安装:

// 安装
npm install -g yo generator-code

// 使用
yo code

运行完以上命令后会出现下面的操作界面,填入需要的信息即可。
clipboard.png

  • 运行示例代码

代码生成后,可以按F5开始调试插件,此时VS Code会新建一个实例并进入插件开发模式,开发中的插件可以在这个新的实例中使用。模版代码会生成一个名为Hello World的命令,按下⇧⌘P调出命令输入窗口,然后输入'Hello World'运行命令。如果找不到命令,重启下VS Code再重新运行。
clipboard.png

  • 修改代码

插件的入口代码在extension.js这个文件中,主要是修改activate函数:

export function activate(context) {

    // Use the console to output diagnostic information (console.log) and errors (console.error)
    // This line of code will only be executed once when your extension is activated
    console.log('Congratulations, your extension "my-first-extension" is now active!');

    // The command has been defined in the package.json file
    // Now provide the implementation of the command with  registerCommand
    // The commandId parameter must match the command field in package.json
    let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
        // The code you place here will be executed every time your command is executed

        // Display a message box to the user
        vscode.window.showInformationMessage('Hello World!');
    });

    context.subscriptions.push(disposable);
}

我插件的功能是用户通过右键点击导航栏,获取选中文件夹的绝对路径,然后提示用户输入新组件的名字,然后自动帮用户生成组件的模板代码。
clipboard.png

clipboard.png

开发的关键点是如何获取文件夹绝对路径和获取用户输入的组件名。尤其是获取路径,找了很久才找到,官网的文档只字未提。先看代码:

function activate(context) {
    console.log('Congratulations, your extension "react-template" is now active!');

    // 注册一个名为createFunctionalComponent的命令
    const fc = vscode.commands.registerCommand('extension.createFunctionalComponent', function (param) {
        // 文件夹绝对路径
        const folderPath = param.fsPath;

        const options = {
            prompt: "请输入组件名: ",
            placeHolder: "组件名"
        }
        
        // 调出系统输入框获取组件名
        vscode.window.showInputBox(options).then(value => {
            if (!value) return;

            const componentName = value;
            const fullPath = `${folderPath}/${componentName}`;

            // 生成模板代码,不是本文的重点,先忽略
            generateComponent(componentName, fullPath, ComponentType.FUNCTIONAL_COMP);
        });
    });
    
    context.subscriptions.push(pc);
}

代码很简单,就不做讲解了。为了显示Create Functional Component这个菜单项,我们需要修改package.json文件的contributes字段。activationEvents字段也要相应的改下。同时为了给插件配一个图标,要加一个icon字段:

    "icon": "images/icon.png",
    "activationEvents": [
        "onCommand:extension.createFunctionalComponent"
    ],
    "contributes": {
        "commands": [
            {
                "command": "extension.createFunctionalComponent",
                "title": "Create Functional Component"
            }
        ],
        "menus": {
            "explorer/context": [
                {
                    "command": "extension.createFunctionalComponent",
                    "group": "1_modification"
                }
            ]
        }
    },

详细的contributes配置可以看这里。配置好menus之后,registerCommand的第二个函数参数就能取到文件或文件夹的绝对路径了。

  • 发布插件

插件开发完后就可以发布了,需要安装vsce

npm install -g vsce

安装完后,需要去Azure DevOps注册并生成一个access token。详细的流程可以看这里。生成toke的时候有两个地方需要注意下:
clipboard.png

token生成后就可以登录和发布了。如果有任何的修改,要注意修改package.json里面版本号才能发布成功。发布成功后,很快就能在VS Code的插件市场搜到了。
clipboard.png

  • 总结

本文介绍了VS Code插件开发的基本流程,实现了一个简单的插件。本文只涉及到VS Code插件系统的冰山一角,更多的高级功能以后接触到的时候再作介绍。如果想再作进一步的了解,可以从这里开始。更多的 VS Code 开发技巧,可以看这个系列的第二篇:VS Code插件开发介绍(二)

查看原文

linshuai 提出了问题 · 7月17日

webview里如何支持PWA

在手机浏览器中,断网之后可以访问到PWA开发的页面,也就是离线缓存生效的。但是在同一手机的webview中,断网之后访问不了缓存,需要怎么样才能支持?

关注 2 回答 1

linshuai 发布了文章 · 5月27日

Vue3 插件开发详解尝鲜版

前言

vue3.0-beta 版本已经发布了一段时间了,正式版本据说在年中发布(直播的时候说的是年中还是年终,网上传闻说是6月份)。嘴上说着学不动,身体却很诚实地创建一个vue3的项目,兴致勃勃地引入 vue2 插件的时候,眉头一皱,发现事情并没有那么简单。

浏览器无情地抛出了一个错误:

Uncaught TypeError: Cannot set property '$toast' of undefined

不是说兼容vue2的写法吗,插件不兼容,这是闹哪样?发下牢骚之后还是得解决问题。研究插件的代码,是这么实现的

var Toast = {};
Toast.install = function (Vue, options) {
    Vue.prototype.$toast = 'Hello World';
}
module.exports = Toast;

vue2 插件的时候会暴露一个 install 方法,并通过全局方法 Vue.use() 使用插件。install 方法的第一个参数是 Vue 构造器,插件的方法就添加在 Vue 构造器的原型对象上。通过 new Vue() 实例化后,在组件中就能调用 this.$toast 使用组件了。(具体实现可以参考我之前的一篇文章:Vue.js 插件开发详解,下面也是基于这个插件demo对比修改)。

而 vue3 的 install 方法不是传入 Vue 构造器,没有原型对象,Vue.prototype 是 undefined,所以报错了。vue3 采用依赖注入的方式,在插件内部利用 provide 和 inject 并暴露出一个组合函数,在组件中使用。

插件实现

基本框架

下面先实现一个插件的基本框架。

import { provide, inject } from 'vue';
const ToastSymbol = Symbol();     // 用Symbol创建一个唯一标识,多个插件之间不会冲突
const _toast = () => {}        // 插件的主体方法
export function provideToast(config) {    // 对外暴露的方法,将 _toast 方法提供给后代组件
    provide(ToastSymbol, _toast);
}

export function useToast() {    // 后代组件可以从该方法中拿到 toast 方法
    const toast = inject(ToastSymbol);
    if (!toast) {
        throw new Error('error');
    }
    return toast;
}

组件使用

在 provideToast 方法中,提供了 _toast 方法,那在根组件中 调用 provideToast 方法,传入插件参数,子组件中就能使用 toast 功能了。

// app.vue 根组件
import { provideToast } from 'vue2-toast';
export default {
    setup() {
        provideToast({
            width: '200px',        // 配置toast宽度
            duration: 2000        // 配置toast持续显示时长
        });
    }
};

在 useToast 方法中,返回了 toast 方法,那在所有的后代组件中调用 useToast 方法,就能拿到 toast 方法了。

// child.vue 子组件
import { useToast } from 'vue2-toast';
export default {
    setup() {
        const Toast = useToast(); // 所有的子组件都要从useToast拿到toast方法
        Toast('Hello World');
    }
};

数据响应

想要控制 toast DOM 元素的显示隐藏,以及提示文本的变化,需要创建响应式的数据,在 vue3 中可以通过 reactive 方法创建一个响应式对象。

const state = reactive({
    show: true,        // DOM元素是否显示
    text: ''    // 提示文本
});

挂载DOM

在页面中显示一个 toast,那就免不了要创建一个 DOM 并挂载到页面中。在 vue2 中是通过Vue.extend 创建一个子类,将子类生成的实例挂载到某个元素中,而 vue3 中通过 createApp 方法来实现的。

const _toast = text => {
    state.show = true;
    state.text = text;
    toastVM = createApp({
        setup() {
            return () =>
                h('div', {
                    style: { display: state.show ? 'block' : 'none' }    // display 设置显示隐藏
                },
                state.text
            );
        }
    });
    toastWrapper = document.createElement('div');
    toastWrapper.id = 'toast';
    document.body.appendChild(toastWrapper);  // 给页面添加一个节点用来挂载toast元素
    toastVM.mount('#toast');
};

完整示例

上面的每一步是这个插件的关键内容,接下来就是完善下细节,比如用户配置插件的全局参数,设置 toast 的样式,持续时间,显示位置等,这里就不一一细讲了,完整示例如下:

import { provide, inject, reactive, createApp, h } from 'vue';
const ToastSymbol = Symbol();

const globalConfig = {
    type: 'bottom', // toast位置
    duration: 2500, // 持续时长
    wordWrap: false, // 是否自动换行
    width: 'auto' // 宽度
};

const state = reactive({
    show: false, // toast元素是否显示
    text: ''
});

let [toastTimer, toastVM, toastWrapper] = [null, null, null];

const _toast = text => {
    state.show = true;
    state.text = text;
    if (!toastVM) {
        // 如果toast实例存在则不重新创建
        toastVM = createApp({
            setup() {
                return () =>
                    h(
                        'div',
                        {
                            // 这里是根据配置显示一样不同的样式
                            class: [
                                'lx-toast',
                                `lx-toast-${globalConfig.type}`,
                                globalConfig.wordWrap ? 'lx-word-wrap' : ''
                            ],
                            style: {
                                display: state.show ? 'block' : 'none',
                                width: globalConfig.width
                            }
                        },
                        state.text
                    );
            }
        });
    }

    if (!toastWrapper) {
        // 如果该节点以经存在则不重新创建
        toastWrapper = document.createElement('div');
        toastWrapper.id = 'lx-toast';
        document.body.appendChild(toastWrapper);
        toastVM.mount('#lx-toast');
    }
    if (toastTimer) clearTimeout(toastTimer);
    // 定时器,持续时长之后隐藏
    toastTimer = setTimeout(() => {
        state.show = false;
        clearTimeout(toastTimer);
    }, globalConfig.duration);
};

export function provideToast(config = {}) {
    for (const key in config) {
        globalConfig[key] = config[key];
    }
    provide(ToastSymbol, _toast);
}

export function useToast() {
    const toast = inject(ToastSymbol);
    if (!toast) {
        throw new Error('error');
    }
    return toast;
}

总结

与 vue2 的插件比较大不同点在于,需要用到 toast 的每个组件中,都需要引入 useToast 方法,看起来会有点麻烦。不过其实这也是 vue 组合式 API 的目的,让代码更可读、更容易被理解,清楚的知道这个方法就是来自这里。正如标题所示,这是尝鲜版,vue3 正式版还未发布,未来会不会有更多的插件形式,咋也不敢问,至少这种还是比较稳的。

查看原文

赞 8 收藏 4 评论 4

linshuai 分享了头条 · 3月13日

前端常见的加密算法介绍

赞 0 收藏 31 评论 0

linshuai 发布了文章 · 3月13日

前端常见的加密算法介绍

一、前言

在信息安全越来越受重视的今天,前端的各种加密也变得更加重要。通常跟服务器的交互中,为保障数据传输的安全性,避免被人抓包篡改数据,除了 https 的应用,还需要对传输数据进行加解密。

目前常见的加密算法可以分成三类

  • 对称加密算法:AES、...
  • 非对称加密算法:RSA、...
  • Hash 算法:MD5、...

二、对称加密算法

对称加密(也叫私钥加密)指加密和解密使用相同密钥的加密算法。它要求发送方和接收方在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信的安全性至关重要。

特点

  • 优点:算法公开、计算量小、加密速度快、加密效率高。
  • 缺点:在数据传送前,发送方和接收方必须商定好密钥,然后双方保存好密钥。如果一方的密钥被泄露,那么加密信息也就不安全了
  • 使用场景:本地数据加密、https 通信、网络传输等

AES

AES:高级加密标准(Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。

image

密钥:用来加密明文的密码。密钥为接收方与发送方协商产生,但不可以直接在网络上传输,否则会导致密钥泄漏,通常是通过非对称加密算法加密密钥,然后再通过网络传输给对方,或者直接面对面商量密钥。密钥是绝对不可以泄漏的,否则会被攻击者还原密文,窃取数据。

在项目中需要用到 AES 加密时,可以使用开源的 js 库:crypto-js

var CryptoJS = require('crypto-js');

var data = { id: 1, text: 'Hello World' };

// 加密生成密文
var ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret_key_123').toString();

// 解密得到明文
var bytes = CryptoJS.AES.decrypt(ciphertext, 'secret_key_123');
var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));

三、非对称加密算法

非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

特点

  • 优点:非对称加密与对称加密相比其安全性更好
  • 缺点:加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
  • 使用场景:https 会话前期、CA 数字证书、信息加密、登录认证等

RSA

RSA 加密算法是非对称加密算法最常见的一种。RSA 是 1977 年由 Ron Rivest、Adi Shamir 和 Leonard Adleman 一起提出的。RSA 就是他们三人姓氏开头字母拼在一起组成的。

image

在项目中需要用到 RSA 加密时,可以使用开源的 js 库:jsencrypt

// 使用公钥加密
var publicKey = 'public_key_123';
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
var encrypted = encrypt.encrypt('Hello World');

// 使用私钥解密
var privateKey = 'private_key_123';
var decrypt = new JSEncrypt();
decrypt.setPrivateKey(privateKey);
var uncrypted = decrypt.decrypt(encrypted);

四、Hash 算法

Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。

简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

特点

  • 优点:不可逆、易计算、特征化
  • 缺点:可能存在散列冲突
  • 使用场景:文件或字符串一致性校验、数字签名、鉴权协议

MD5

MD5 是比较常见的 Hash 算法,对于 MD5 而言,有两个特性是很重要的,第一:明文数据经过散列以后的值是定长的;第二:是任意一段明文数据,经过散列以后,其结果必须永远是不变的。前者的意思是可能存在有两段明文散列以后得到相同的结果,后者的意思是如果我们散列特定的数据,得到的结果一定是相同的。

比如在登录时将密码进行 md5 加密再传输给服务器,服务器中的密码也是用 md5 加密后存储的,那么只要验证加密后的密文是否一致则可。

在项目中需要用到 MD5 加密时,可以使用开源的 js 库:JavaScript-MD5

var hash = md5('Hello World');
// b10a8db164e0754105b7a99be72e3fe5

五、Base64 编码

Base64 编码只是一种编码格式并不是加密算法,它可用于在 HTTP 环境下传递较长的标识信息。

特点

  • 可以将任意的二进制数据进行 Base64 编码
  • 数据加密之后,数据量会变大,变大 1/3 左右
  • 编码后有个非常显著的特点,末尾有个=号
  • 可进行反向解码
  • Base64 编码具有不可读性

现代浏览器都提供了 Base64 编码、解码方法,btoa() 和 atob()

var enc = window.btoa('Hello World');
// SGVsbG8gV29ybGQ=

var str = window.atob(enc);
// Hello World

六、总结

在业务 http 请求中,AES 的密钥在前端随机生成,从服务器获取 RSA 的公钥,对 AES 的密钥进行非对称加密,把加密后的密钥在请求头中传给服务器,用 AES 对 body 进行加密。服务器收到请求头中的加密后的密钥,用 RSA 的密钥进行解密,得到明文的 AES 密钥,即可对 body 进行解密。md5 有校验字符串一致性的特性,为避免请求被拦截后篡改 body,可在发请求时,将 body 字符串进行一个 md5 加密后在请求头传输,服务器收到请求后,解密 body 后再 md5 与请求头的进行校验,可验证是否请求被篡改。

查看原文

赞 38 收藏 31 评论 3

linshuai 提出了问题 · 2019-11-27

各位大佬有没有基于node的开源论坛推荐啊

想自己搭一个公司内部论坛,有没有好推荐的,已知nodeclub和nodebb,还有没有其他可推荐的啊

关注 3 回答 2

linshuai 发布了文章 · 2019-11-06

Rollup.js: 开源JS库的打包利器

前言

Rollup 是一个 JavaScript 模块打包器,说到模块打包器,自然就会想到 webpack。webpack 是一个现代 JavaScript 应用程序的静态模块打包器,那么在 webpack 已经成为前端构建主流的今天,为什么还要用 Rollup 呢?

Rollup 中文文档 中介绍到,它可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。可以看到它的应用场景之一,就是打包 JS 库。自己写个 JS 库,在我们开发工作中和开源项目中还是比较常见的。可谓是生命不息,造轮子不止。如果还没写过,那就赶紧来提升下自己的技(逼)术(格)吧。

对比 webpack

用过 webpack 的都知道,它可以将所有的静态资源,导入到应用程序中,也是因为它强大的功能,所以打包 bundle 文件时,会产生很多冗余的代码,在大型的应用中,这点冗余代码就会显得微不足道,但是在一个小小的库中,就会显得比较明显了。比如这么一个类:

class People{
    constructor(){
        this.name  = 'linxin'
    }
    getName(){ return this.name; }
}
export default People;

经过 webpack 打包之后,变成下面这样(案例移除了具体内容),多出了很多方法,这显然不是我们想要的。

/******/ (function(modules) { // webpackBootstrap
/******/     var installedModules = {};
/******/     function __webpack_require__(moduleId) { **** }
/******/     __webpack_require__.m = modules;
/******/     __webpack_require__.c = installedModules;
/******/     __webpack_require__.d = function(exports, name, getter) { *** };
/******/     __webpack_require__.r = function(exports) { *** };
/******/     __webpack_require__.t = function(value, mode) { *** };
/******/     __webpack_require__.n = function(module) { *** };
/******/     __webpack_require__.o = function(object, property) { *** };
/******/     __webpack_require__.p = "";
/******/     return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/******/ ([ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__);
class People{
    constructor(){
        this.name  = 'linxin'
    }
    getName(){
        return this.name;
    }
}
/* harmony default export */ __webpack_exports__["default"] = (People); }) ]);

而 Rollup 打包之后的代码跟源码基本一致,作为一个 JS 库,我们还是希望简洁一点,代码量少点。毕竟实现相同的功能,谁都不想去引入一个更繁重的库吧。

特性

ES模块

Rollup 使用 ES6 的模块标准,而不像 CommonJS 和 AMD,虽然也叫模块化,其实只是一种临时的解决方案。Rollup 的模块可以使我们开发时可以独立的开发每个小模块,代码小而简单,更加方便测试每个小模块,在构建时才打包成一个完成的 JS 库。

Tree-shaking

tree shaking 指的是移除 JavaScript 上下文中的未引用代码,它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。静态结构的 import 就好像是变量引用一样,不需要执行代码,在编译时就可以确定它是否有引用到,如果没引用,就不把该段代码打包进来。比如用到了一个第三方库的一个功能,但我肯定不要把它完整的库都打包进来,我只需要打包用到的代码即可,这时候 tree shaking 就可以发挥出它的作用了。

应用

开发一个 JS 库,我需要 Rollup 能为我提供些常用的功能:

  • 支持 ES6 转 ES5
  • 代码压缩
  • ESLint
  • 支持 Typescript

基本配置

Rollup 使用一个 rollup.config.js 文件进行配置。

// rollup.config.js
export default {
    input: 'src/index.js',
    output: {
        file: 'dist/bundle.js',
        format: 'umd'
    }
};

配置跟其他工具基本一致,从入口文件 index.js 打包后输出文件 bundle.js。format 是生成包的格式,可选有 amd,cjs,es,iife,umd,umd 是通用模块定义,打包后可以通过 <script> 标签引入,也可以通过 import 等方式引入,作为一个 JS 库要适用各个场景,应选择 umd 。

babel

Rollup 通过插件在打包的关键过程中更改行为,babel的插件就是 rollup-plugin-babel,需要先安装相关依赖

npm i rollup-plugin-babel@latest @babel/core @babel/preset-env -D

新建 .babelrc 文件,配置 babel

{
    "presets": ["@babel/preset-env"]
}

代码压缩

npm i rollup-plugin-uglify -D

因为 rollup-plugin-uglify 无法压缩 ES6 的语法,所以必须先用 babel 转。如果想直接压缩 ES6 的语法,可换成 rollup-plugin-terser

ESLint

开发一个 JS 库,不能乱七八糟,随心所欲的写代码,必须规范起来,当别人为你的开源库做贡献时,也必须遵循你的开发规范。安装 ESLint 插件

npm i rollup-plugin-eslint -D

然后初始化生成一个 ESLint 配置文件 ./node_modules/.bin/eslint --init

那么最终的 rollup.config.js 配置文件如下:

import babel from 'rollup-plugin-babel';
import { uglify } from 'rollup-plugin-uglify';
import { eslint } from "rollup-plugin-eslint";
export default {
    input: './index.js',
    output: {
        file: 'dist/bundle.js',
        name: 'People',
        format: 'umd'
    },
    plugins: [
        eslint({
            fix: true,
              exclude: 'node_modules/**'
        }),
        babel({
          exclude: 'node_modules/**'
        }),
        uglify()
    ]
};

TypeScript

如果使用 TypeScript 进行开发,则需要安装 rollup-plugin-typescript2 插件和相关依赖

npm i rollup-plugin-typescript2 typescript -D

然后初始化生成一个 tsconfig.js 配置文件 tsc --init,那么使用 TypeScript 的打包文件如下:

import typescript from 'rollup-plugin-typescript2';

export default {
    input: './index.ts',
    output: {
        file: 'dist/bundle.js',
        name: 'People',
        format: 'umd'
    },
    plugins: [
        typescript()
    ]
}

插件

除了以上用的这些插件之外,还有一些可能根据项目需求也有需要

  • rollup-plugin-commonjs:让 Rollup 识别 commonjs 类型的包,默认只支持导入ES6
  • rollup-plugin-node-resolve:让 Rollup 能识别 node_modules 中的包,引入第三方库,默认识别不了的
  • rollup-plugin-json:支持 json 文件
  • rollup-plugin-replace:支持字符串替换
  • rollup-plugin-sourcemaps:能生成 sourcemaps 文件

总结

以上只是介绍了 Rollup 的一些基本用法,更多的请参考官方文档。Rollup 已被许多主流的 JavaScript 库使用,包括 vue 和 react。它也可用于构建绝大多数应用程序,但是代码拆分和运行时态的动态导入这类高级功能,它还不能支持,如果需用这些功能,那就可以使用 webpack。案例可查看:sChart.js

查看原文

赞 10 收藏 5 评论 0

linshuai 回答了问题 · 2019-09-05

泰语在html页面中如何断字换行?

找到解决方法了吗

关注 1 回答 2

linshuai 评论了文章 · 2019-05-11

旗帜鲜明地抵制 CSDN 下载(盗版)站!

SegmentFault 上线付费课程以来,对于内容质量一直严格把关,讲师认真备课,课后为学员答疑,广受好评。然而近期有多位 SegmentFault 讲师反馈在 CSDN 下载频道出现了大量他的盗版课程。

clipboard.png

不查不知道,一查我们发现——我们讲师辛辛苦苦花了上百个小时录制的付费课程,在 CSDN 下载频道竟有满满一屏幕的盗版存在(相关证据我们已经找律师团队取证),同时根据他们的关键词推荐我们发现在其博客频道也有大量的盗版内容,防不胜防,让人不吐不快。

clipboard.png

昨天我在朋友圈公开这个事情后,收到了大量业内同行的反馈,我们发现不仅仅是 SegmentFault,几乎我认识的所有同行以及我们熟悉的讲师的付费内容(包括不限于课程、图书、专栏)都有被 CSDN 下载频道侵权的经历,昨天,我们用了同行的一些关键词在 CSDN 下载频道进行检索,同样发现存在有大量的盗版内容存在。

clipboard.png

同时我们也收到一些用户的反馈在 CSDN 下载频道有大量 IT 相关技术书籍的扫描版文件,毫无疑问都是盗版资源。上传时期从 2016 年(或者更早)开始到至今一直存在。

我们通过关键词在 Google 检索后,发现最早控诉 CSDN 下载频道的内容《CSDN 首页鼓励盗版图书下载》是在 2005 年发布,在知乎等社区也有大量的讨论,如《如何看待 CSDN 利用用户上传的盗版资料卖积分赚钱?》《为什么 CSDN 能做到让用户花钱买积分下载自己网站的盗版资源?》……可见已在业内引起公愤。

clipboard.png

CSDN 做为中国最早的技术社区之一,我们认可其对开发者之间线上交流做出的贡献,但是其下载频道的存在大大助长了大量盗版侵权内容的产生。并非个例,长期存在,越发泛滥,从未被解决——这代表其产品存在根本上的机制问题。

CSDN 官方对此不知情吗?

不过是睁一只眼闭一只眼罢了——CSDN 的下载频道占了总社区超过 30% 的流量。靠着盗版和侵权他人优质内容,获取平台流量,再依靠平台流量进行广告和其他形式的变现。这不是非法牟利是什么?

我们不禁质疑号称中国最大技术社区的 CSDN 究竟拥有怎样的价值观?

纵容盗版,非法牟利;

无视用户隐私——曾经明文存储用户名密码导致用户数据泄露;

甚至欺骗客户——夸大网站流量,广告数据造假。还记得去年程序员广告代码刷量的乌龙事件吗?“博客详情页PC增加广告系统刷量代码”这句话写在了代码注释里面上线了。原来客户的广告数据都是刷出来的?(心疼投放广告的客户)

其实技术圈子非常小,很多同行可能碍于面子或者各种各样的原因,没有公开地去声讨 CSDN,一些声讨可能也并没有解决问题,这更助长了其纵容盗版的气焰。 SegmentFault 作为技术社区的一员,我们深知社区发展的不易,我们有责任帮助我们的讲师维护其付费内容的版权不被侵犯。

我们已经聘请专业的律师团队取证,同时我们也呼吁被 CSDN 下载站侵犯过内容版权的同行,讲师,书籍作者,广大开发者一起发声,在评论区留下你的声音和被侵权经历,通过曝光侵权甚至违法行为,共同净化行业环境。

一起举报

到中央网信办(www.12377.cn)

国家新闻出版广电总局官网(www.sapprft.gov.cn)

举报 CSDN下载站的盗版侵权行为。

图片描述

SegmentFault CEO:高阳Sunny 2019年5月9日凌晨于北京

查看原文

linshuai 赞了文章 · 2019-05-11

旗帜鲜明地抵制 CSDN 下载(盗版)站!

SegmentFault 上线付费课程以来,对于内容质量一直严格把关,讲师认真备课,课后为学员答疑,广受好评。然而近期有多位 SegmentFault 讲师反馈在 CSDN 下载频道出现了大量他的盗版课程。

clipboard.png

不查不知道,一查我们发现——我们讲师辛辛苦苦花了上百个小时录制的付费课程,在 CSDN 下载频道竟有满满一屏幕的盗版存在(相关证据我们已经找律师团队取证),同时根据他们的关键词推荐我们发现在其博客频道也有大量的盗版内容,防不胜防,让人不吐不快。

clipboard.png

昨天我在朋友圈公开这个事情后,收到了大量业内同行的反馈,我们发现不仅仅是 SegmentFault,几乎我认识的所有同行以及我们熟悉的讲师的付费内容(包括不限于课程、图书、专栏)都有被 CSDN 下载频道侵权的经历,昨天,我们用了同行的一些关键词在 CSDN 下载频道进行检索,同样发现存在有大量的盗版内容存在。

clipboard.png

同时我们也收到一些用户的反馈在 CSDN 下载频道有大量 IT 相关技术书籍的扫描版文件,毫无疑问都是盗版资源。上传时期从 2016 年(或者更早)开始到至今一直存在。

我们通过关键词在 Google 检索后,发现最早控诉 CSDN 下载频道的内容《CSDN 首页鼓励盗版图书下载》是在 2005 年发布,在知乎等社区也有大量的讨论,如《如何看待 CSDN 利用用户上传的盗版资料卖积分赚钱?》《为什么 CSDN 能做到让用户花钱买积分下载自己网站的盗版资源?》……可见已在业内引起公愤。

clipboard.png

CSDN 做为中国最早的技术社区之一,我们认可其对开发者之间线上交流做出的贡献,但是其下载频道的存在大大助长了大量盗版侵权内容的产生。并非个例,长期存在,越发泛滥,从未被解决——这代表其产品存在根本上的机制问题。

CSDN 官方对此不知情吗?

不过是睁一只眼闭一只眼罢了——CSDN 的下载频道占了总社区超过 30% 的流量。靠着盗版和侵权他人优质内容,获取平台流量,再依靠平台流量进行广告和其他形式的变现。这不是非法牟利是什么?

我们不禁质疑号称中国最大技术社区的 CSDN 究竟拥有怎样的价值观?

纵容盗版,非法牟利;

无视用户隐私——曾经明文存储用户名密码导致用户数据泄露;

甚至欺骗客户——夸大网站流量,广告数据造假。还记得去年程序员广告代码刷量的乌龙事件吗?“博客详情页PC增加广告系统刷量代码”这句话写在了代码注释里面上线了。原来客户的广告数据都是刷出来的?(心疼投放广告的客户)

其实技术圈子非常小,很多同行可能碍于面子或者各种各样的原因,没有公开地去声讨 CSDN,一些声讨可能也并没有解决问题,这更助长了其纵容盗版的气焰。 SegmentFault 作为技术社区的一员,我们深知社区发展的不易,我们有责任帮助我们的讲师维护其付费内容的版权不被侵犯。

我们已经聘请专业的律师团队取证,同时我们也呼吁被 CSDN 下载站侵犯过内容版权的同行,讲师,书籍作者,广大开发者一起发声,在评论区留下你的声音和被侵权经历,通过曝光侵权甚至违法行为,共同净化行业环境。

一起举报

到中央网信办(www.12377.cn)

国家新闻出版广电总局官网(www.sapprft.gov.cn)

举报 CSDN下载站的盗版侵权行为。

图片描述

SegmentFault CEO:高阳Sunny 2019年5月9日凌晨于北京

查看原文

赞 143 收藏 1 评论 57

linshuai 回答了问题 · 2019-03-20

解决js字符串去除指定字符

replace 会返回一个新的字符串而不会改变原有字符串,所以你那样写肯定就不会得到想要的结果。

关注 7 回答 8

linshuai 回答了问题 · 2019-03-01

解决请教vue异步返回数据问题

getList: function (id){
            this.getAxios('/demo/index',{id:id}).then(res => {
                console.log(res);
            });
            
        },
        
        getAxios: function(url,data){
            let _this = this;
            return axios({
                method:'get',
                url:url,
                params:data,
            });
        }

关注 3 回答 4

linshuai 赞了文章 · 2019-02-26

开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?

开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?

原创: 嘉宾-张楠

开源中国

以往我们说某一功能跨多端,往往是指在诸如 PC、移动等不同类型的设备之间都能实现;或者更加具体一点,指的是“跨平台”,可能是大到跨操作系统,比如 Windows、macOS、Linux、iOS 与 Android 等,可能是小到跨某个具体技术的不同实现库。

但是今天我们要介绍的是关于跨 MVVM 架构模式各种环境的场景。

clipboard.png

Chameleon 是一套开源跨端解决方案,它的目标是让 MVVM 跨端环境大一统,实现任意使用 MVVM 架构设计的终端,都能使用其进行开发并运行。

在这样一个 MVVM 环境中,涉及到了 Weex、React-Native、WebView/浏览器与 Flutter 等各种跨端技术,还有它们实现的具体业务产品,比如微信小程序、快应用、支付宝小程序、百度智能小程序、今日头条小程序与其它各类小程序。

clipboard.png

也许你发现了,这里提到了许多种“小程序”,虽然最早微信小程序的概念甚至早期版本出现的时候,有过不少不看好的声音,但是随着它不断发展,目前已经成为了大众生活不可或缺的应用形态。

马化腾透露过,截至 2018 年 11 月有 150 万微信小程序开发者,小程序应用数量超过 100 万,覆盖 200 多个细分行业,日活用户达到 2 亿。这样的成功经验与几乎触及到生活方方面面的巨大流量入口,大家都想入场,于是可以看到后来其它公司纷纷给出了类似的小程序方案。

另一方面,除了小程序百花齐放,2018 年小米、华为、OPPO 等 10 家安卓手机厂商还结成了快应用联盟,并且先后发布了一系列快应用。

Chameleon 目标就是要跨这些端,而随着各家不同实现越来越多,跨端场景也不断变得更加复杂。我们采访了 Chameleon 创始人张楠,请他为读者具体分享了 Chameleon 在这个过程中的成长。

项目地址:https://github.com/didi/chame...

本文是 Chameleon 首次对外公开实现原理!

干货超多,包括:

  • 终端开发未来的开发模式
  • Chameleon 跨端实现原理
  • 当前各种跨端方案原理对比(各种小程序、快应用等)
  • 与 Taro 的对比
  • 演进过程中遇到的困难与思考

当初为什么去研发 Chameleon?

关于这个问题可以从行业背景讲起。

中国互联网络信息中心(CNNIC)发布的《中国互联网络发展状况统计报告》显示,截至 2018 年 6 月,我国网民规模达 8.02 亿人,微信月活 10 亿 、支付宝月活 4 亿、百度月活 3.3 亿;另一方面,2018 Q3 中国 Android 手机占智能手机整体的比例超过 80%,月活约 6 亿。

BAT 与 Android 成为了中国互联网真正的用户入口。但凡流量高的入口级别 APP 都希望做平台,成为一个生态平台和互联网流量入口,大量第三方应用的接入,从业务层让公司 APP 关联上更多企业的利益,并且拥有更强的生命力;从技术层面可以利用“本地能力接口层”收集大量用户数据,从消费互联网到产业互联网需要大量各行各业基础用户数据线索进行驱动和决策。

在这么一种背景下,再结合计算机技术的发展历史,我们知道每一种新技术的出现都会经历“各自为政”的阶段,小程序技术也不例外,所以我们看到了其它各种小程序平台出现。

微信小程序作为首创者,虽然其它小程序都有在技术实现原理、接口设计上刻意模仿,但是作为一线开发者在不同平台发布小程序,往往还是需要重复开发、测试,从前 1 单位的工作量变成了 N 单位的工作量。而这还没算上快应用等其它入口。

这种情况下,滴滴的研发工程师是其中最显著的“受害者”之一,滴滴出行在微信钱包、支付宝、Android 快应用都有相关入口,而且用户流量占比不低。

研发同学在端内既追求 H5 的灵活性,也要追求性能趋近于原生。面对入口扩张,主端、独立端、微信小程序、支付宝小程序、百度小程序、安卓厂商联盟快应用,单一功能在各平台都要重复实现,开发和维护成本成倍增加。

迫切需要一个只维护一套代码就可以构建多入口的解决方案,于是我们着手去打造了 Chameleon(CML,卡梅龙)这么一个项目,真正专注于让一套代码运行多端。

Chameleon 核心是运用了 MVVM 架构,为什么它可以实现跨多端?

MVVM 也就是 Model View ViewModel,它本质上是 MVC( Model View Controller)的进化版本,将 View 的状态和行为抽象化,使得视图 UI 和业务逻辑分开。

它是一种让数据驱动反射视图的模式,发展到现在可能会偏离它的初衷了,更像是一个视图数据间的“通信协议”,让终端开发变得更加单纯,这是一种趋势,面向未来框架都采用这种模式。

clipboard.png

Facebook 在 2013 年开源 React,React 这个项目本身是一个 Web UI 引擎,随着不断发展,它衍生出 React Native 项目,用来编写原生移动应用。正是它给跨端方向带来了 MVVM 模式。

Vue.js 于 2014 年左右发布,逆流而上占据了大量用户群体,2016 阿里巴巴也基于它发布了 Weex 项目,使得可以用 Vue 编写 Native App。

Google 在 2018 年末正式发布了面向未来的跨 Android、iOS 端的 Flutter 1.0.0。

原理

我们知道终端开发离不开三大要素——界面表现(结构、外观)层、逻辑处理层与系统接口层(网络、存储与媒体等)。

clipboard.png

开发者编写代码时在初始化阶段(生命周期)调用“界面表现层”界面模型的接口绘制界面,当用户触摸界面时,“界面表现层”将事件发送给用户“逻辑处理层”,后者经过条件判断再处理并反馈到用户界面,处理过程可能需要调用“系统接口层”,反馈过程需要调用“界面表现层”的接口。

常规的终端开发架构模式下,无论是 Web 端、Android 端还是 iOS 端的项目开发,都强依赖各端的环境接口,特别是依赖界面相关模型设计。

iOS 系统下绘制界面基于 Objective-C 语言环境下的 UIKit 框架;Android 系统下用户绘制界面基于 Java 语言环境,由 LayoutInflater 处理 XML 结构层次树;Web 端使用 DOM 模型和 CSS 来描述绘制界面。  

MVVM 中的关键是它通过 ViewModel 这一层将界面和逻辑层彻底隔离开来,负责关联界面表现和逻辑处理层的响应事件(update/notify)关系,这一“隔离层”上下通信足够规范、足够纯净单一。  

Model 进行逻辑处理是纯业务响应逻辑,任何一种语言都可以实现,你可以用 Android 的 Java,也可以用 iOS 的 Objective-C,你心情好用“世界第一语言 PHP”也能实现。

之所以普遍选择 JavaScript,很大程度是因为在这个领域内它的优点显著,如学习成本低、天生具备跨端属性、虚拟机(V8、JavaScriptCore)和各方向组件建设较好、生态活跃。

而系统接口层则更简单了,只需穷举统一基础接口+可扩展接口能力即可。

各种 MVVM 方案

具体来看看各种 MVVM 方案都是怎么样的。

React Native、Weex 与快应用的 MVVM

clipboard.png

开发者编写的代码在虚拟机(V8、JavaScriptCore)里面运行,虚拟机容器里面包含扩展的系统基础接口。运行时,将描述界面的数据(主要是 CSS+DSL 所描述内容)通过通信层传递给 Android、iOS 端的渲染引擎,用户触摸界面时,通过通信层传递给虚拟机里面的业务处理代码,业务处理代码可能调用网络、储存与媒体等接口,最后再次反馈到界面。

Flutter 的 MVVM

Flutter 和 RN 的最大区别在于将“JavascriptCore/V8+JS”替换成“C++ 实现的 engine+Dart 实现的 Framework+静态类型 Dart+编译成机器码”。

Flutter 的方案如下图所示:

clipboard.png

Service 其实就是本地能力接口层,Widget 树是视图层模型。

clipboard.png

Flutter 和 RN 的使用面设计上类似,Flutter 文档中提到“In Flutter, almost everything is a widget.”,widget 的调用从 RN 的 JSX 变成 Flutter 的 widget 调用,UI 的外观描述从 RN 的 CSS(文本样式、布局模型、盒模型)到定制化 Flutter Widget(textStyle 、Layout Widget、Widget)。

clipboard.png

本质上 Flutter 也是 MVVM 架构,逻辑层通过 setState 通知视图层更新,一定程度上这也是为什么 Flutter 敢说能转成 Web 框架的原因,核心还是基于这类数据驱动视图架构模式,业务代码不会深度依赖任何一端特有的“视图模型”。

各类小程序的 MVVM

小程序本质上和 Weex、React Native 的设计思路基本一样,最大区别在于前者还是用浏览器 WebView 做渲染引擎,而后者是单独实现了渲染引擎(所以大量的 CSS 布局模型不支持)。

clipboard.png

具体到 Chameleon 上是怎么实现的?

首先任何一份应用层的高级语言代码块分成几层:语言层(Language)、框架层(Framewrok)与库层(Library):

  • Language —— 通俗来说,实现程序所需的基本逻辑命令:逻辑判断(if)、循环(for)与函数调用(foo())等。
  • Framewrok —— 通俗来说,完成一个 App 应用交互任务所需规范,例如生命周期(onLoad、onShow)、模块化与数据管理等。
  • Library —— 可以理解就是“方法封装集合”。比如 Web 前端中 Vue 更适合叫框架,而 jQuery 更适合叫库;Android 系统下 activity manager + window Manager  View System 等的集合叫框架,而 SQLite 、libc 更适合叫库。

对应到 Chameleon 就是这样:

clipboard.png

具体到实现原理全景架构图如下:

clipboard.png

你可以理解 Chameleon 为了实现“让 MVVM 跨端环境大统一”的目标做了以下工作:

  • 定义了标准的 Language(CML DSL)、Framework 与 Library(内置组件和 API)协议层。
  • 在线下编译时将 DSL 转译成各端 DSL,只编译 Language 层面足够基础且稳定的代码。
  • 在各个端运行时分别实现了 Framework 统一,在各个端尽量使用原有框架,方便利用其生态,这样很多组件可以直接用起来。
  • 在各个端运行时分别实现了 Library(内置组件和 API)。
  • 为用户提供多态协议,方便扩展以上几方面的内容,触达底层端特殊属性,同时提升可维护性。

clipboard.png

实现思路很简单,所有设计为了 MVVM 标准化,不做多余设计,所以宏观的角度就像 Node.js(libuv)同时运行在 Windows 和 macOS 系统,都提供了一个跨平台抽象层。

从 MVVM 角度来看的话:

  • View(展现层)
  • 第三方 Render Engine:各类框架已有框架,浏览器的 Vue、Webview 里的小程序引擎、Android、iOS 里面的 React Native/Weex 引擎、甚至 Flutter 里面的 Dart Framework。
  • Chameleon 内置组件库:多态协议定义统一组件 view、input、text、block 与 cell 等,它是界面组层的原始基类,衍生出多复杂界面功能。
  • ViewModel(关联层)Chameleon 语法转译
  • 组件调用
  • 循环
  • 条件判断
  • 事件回调关联
  • 父子关系
  • ……
  • Model(逻辑响应层)
  • JavaScript 代码
  • CML Runtime 框架
  • Chameleon API:多态协议定义统一接口,cml.request、cml.store 等

Chameleon 的跨多端方案给开发者的开发带来了极大的便利,具体表现是怎么样的?

一句话:基于 Chameleon 开发,效率会越来越高

各个端的涌现,让原本是 1 的工作量因为多端存在而变成 N 倍,使用 Chameleon,工作量会变回 1.2。这多出来的 0.2 工作量是要处理各端的差异化功能,比如以下场景:

  • 某业务线迁入 Chameleon 时,发现没有“passport登录组件”,在各类小程序里面能免密登录了,在 Web、Native 端是弹出登录框登录,不同业务用户交互形态不一样所以 Chameleon 没有提供组件;开发者需要基于多态协议扩展单独一个登录组件<passport/>,无论如何最后返回一个登录后的回调 token 即可,外部无需组件关心里面如何操作。
  • 用户需要分享功能,发现没有“share组件”,在微信 Web 端可以引导右上角分享,在小程序直接分享,不同业务用户交互形态不一样,用户需要基于多态协议扩展单独一个登录组件<share/>。

这种各端差异较大的例子,随着业务的积累,可以变成了一个个业务组件单独维护,后面也不需要重复开发了,且反推产品体验一致化,组件三层结构“CML框架内置组件->CML扩展组件->业务开发者自己扩展的多态组件”达成 100% 统一。

随着组件积累业务开发工作量越来少,工程师可以专注做更加有意义的事情,这就是 Chameleon 存在的目的。

基于统一的跨端抽象,用户在 Chameleon 项目持续维护过程中,Chameleon 发布新增一个端之后,你的业务代码基本不用改动即可无缝发布成新端。

比如这个 cml-yanxuan 项目开发时支持 3 个端,后面新增了百度、支付宝小程序端,原有代码直接能跑起来运行 5 个端,一端所见即多端所见。

开发时只能跑 3 个端

clipboard.png

原有代码无缝支持 5 个端

clipboard.png

另外特别强调的是,对于大公司团队,如果有很强的技术能力,希望开发的代码掌控在自己手里,对输出结果有更好控制能力。其实 Chameleon 内置组件和内置 API 是可以替换的,那么所有组件都是业务方自己开发了,哪天不想用了直接导出原生组件即可离开 Chameleon,如下图:

clipboard.png

目前跨多端统一的方案中,Taro 是比较亮眼的,能否具体对比一下 Chameleon 与 Taro。

我们觉得 Chameleon 与其它解决方案的最大区别在于其它框架都是小程序增强,即用 Vue 或者 React 写小程序,这些框架官方给的已接入例子也都是跑微信小程序。

它们更加类似 Chameleon 的前身 MPV(Mini Program View),即考虑如何增强小程序开发。2017 年微信小程序发布时,滴滴作为白名单用户首先开始尝试接入,开始面对重复开发的难题。

这时候我们专门成立了一个小项目组,完成一个名为 MPV 的项目,一期目标是“不影响用户发挥,不依赖框架方的原则性实现一套代码运行 Web 和微信小程序”。

看着很美好,用这样的方案实现 Web 端和小程序端,也确实完成了超过 90% 代码重用,总体上开发效率和测试效率都有了一定提升,但是却不是真正意义上的跨多端统一。

单独说到 Chameleon 与 Taro 的区别,总体上看,可以归为这样一个表:

clipboard.png

表中每一项都是在做跨端方案时需要考虑到的。我们说除了 Chameleon,其它方案都只是在对小程序进行增强,或者说是模仿微信小程序的 API 和组件的接口设计。

Taro 是通过将 JSX 转成小程序模板,在其它端模拟微信小程序的接口和组件,让其它端更像微信小程序,业务开发时不一致的地方需要环境变量判断差异分别调用,会造成端差异逻辑和产品逻辑混合在一起。

此外,它要跟随小程序更新,业务方会有双重依赖;其它端的和小程序不能保持一致,用户要各种差异化兼容,不利于维护。

那 Chameleon 呢?Chameleon 把这些问题都考虑到了,所以在早期伪跨端 MiniProgram View 成型之后不断演进的过程中,把它发展成为一个真正的跨多端方案。

前边的表格显示了,Chameleon 既考虑统一性,又考虑差异性,且差异性不会影响可维护性;当各端差异确实太大,那就不要用一套代码实现多个端同一页面,而是统一公用组件。

这还只是拿 Chameleon 与 Taro 的重合点进行了对比,但是别忘了 Chameleon 不仅仅是前端框架,它:

  • 还有统一的 Chameleon Native  SDK,Chameleon 不仅仅希望统一各类小程序,还要覆盖自家 APP,会持续通过 Native SDK 扩展 API 和组件,期望有与小程序一样的本地能力。理想情况下,一套代码就能在各类小程序、自家 APP 里面无缝平滑运行。
  • 还有待开源的后台管理系统。
  • 还有待开源的 XEdtior 非研发用编辑器,可以直接编辑跨端页面、直接发布。

另外,未来还将带来以下能力:

  • 后端统一接口(消息推送、分享与支付等)
  • 基于统一的 MVVM 标准,更有基于 Flutter 的原生 APP

当前的各类小程序和 Native 跨端框架,类似当年多个浏览器时,Safari、Chrome、Firefox、IE 6/7/8/9、Android 浏览器等盛行的时代。以这个来类比,那么 Chameleon 的接口组件设计上更像一个 jQuery。

网络请求有的是 XHRHttprequest 有的是 ActiveXObject,jQuery 考虑的是用户需要什么,需要一个网路请求接口,支持 get、post 等,所以 jQuery 写一个既非 ActiveXObject 又非 XHRHttprequest 的名为 $.ajax 接口,提供一个封装网络接口,你不用关心内部在不同端怎么调用的,jQuery 内部会帮你兼容。

Chameleon 也是一样的思路,所有的接口设计都是真正能兼容跨所有的端,没有差异性,而且只保留当前所在端的接口调用代码:IE 里面只保留 ActiveXObject,Chrome 只保留 XHRHttprequest。

Chameleon 的接口设计上比 jQuery 更强的地方在于,使用标准的多态协议,保障可维护性,性能上只保留当前端代码,且将多态协议暴露出来,让用户也能扩展自己想要的 API(类比 $.xxx)。

当然时代已经变了,监听视图不在是 $('#xxx').click(fn),而是 MVVM 数据驱动视图方式了,所以提供了 Chameleon 双向绑定这样的 VM 层。

前边讲到了 Chameleon 的前身 MPV,那具体分享一下 Chameleon 的整个演进过程吧。

出生期:选择转译还是模拟小程序环境?

前面讲到,2017 年的时候,我们完成一个名为 MPV 的项目,一期目标是不影响用户发挥,不依赖框架方的原则性实现一套代码运行 Web 和微信小程序。

当时缺乏小程序资料是遇到的最大问题(就更别提今天讲到的业内这么多解决方案了),当时唯一一个可以参考的开源项目是 WEPT,WEPT 是一个微信小程序实时开发环境,它的目标是为小程序开发提供高效、稳定、友好、无限制的运行环境。它的设计思路是在 Web 端模仿小程序环境执行。

于是我们在开发 MPV 时考虑了两种实现策略:

1、在 Web 端像 WEPT 一样 mock 小程序环境;就像微信开发者工具里面也模拟了小程序执行环境,WAServie、WAWebview 提供的两套环境源码做底层,在页面中开启三个独立运行环境运行并用 iframe 通讯模拟微信小程序的 3 个 Webview 之间的联通关系。

2、逐个转译代码支持小程序,缺点是可能会有 edge case 需要处理以及潜在的 bug 会比较多。

最终在看完 WEPT 源码和微信开发者工具的情况下,我们明确放弃了第 1 条实现策略,选择了逐个转译代码支持小程序的路线,主要原因是于 Web 端兼容微信所有的功能,尺寸过于庞大。

经过三个月紧锣密鼓的开发终于实现了第一版本 MPV:  

clipboard.png

经过实现几个 demo 之后,开始执行迁移计划:

clipboard.png
 

MPV 在 Webapp 上实践最终实现效果如下:

clipboard.png

最终实现效果挺美好,也确实完成了超过 90% 的代码重用,总体上开发效率和测试效率都有了明显提升。

但是在后续实践过程中,发现存在大量的问题,并且项目越大问题越凸显出来,总结如下:

  • 可维护性问题,没有隔离公用代码和各端差异代码。项目中不止有业务逻辑,还混杂着 Web 端和小程序端产品功能差异化逻辑。比如前边举过的例子,分享功能 Web 端无法实现(引导分享),小程序可以实现,意味着各种环境判断各种差异化逻辑,牵一发动全身,还要来回测试。
  • 方向选择错误,MPV 使用了小程序语法标准(小程序的生命周期、API 接口等),导致用户使用上无法清晰理解。
  • 不能直接使用各端已有生态组件,即缺乏标准规范接入某个端已有开源组件。比如 Web 端 pick.js 组件缺乏快速接入规范,用户要么重新开发,或者在模板和 js 代码中使用环境判断的方式针对引入。最终导致同一功能不同端的调用方式、输入与输出不一致。
  • 业务项目依赖 MPV 框架。框架依赖微信小程序接口(模板、生命周期与接口),扩展了统一接口。例如微信小程序更新了 wx.request 时,业务项目方无法立刻使用,需要等框架更新。
  • 文件夹结构混乱,混杂着多个端代码文件,且识别成本高。
  • 不支持 vuex、redux 等高效数据管理方式
  • 尺寸单位不统一,px 和 rpx 不一致
  • 周边小型差异点太多:
  • 协议不一致,例如 Web 端可以用 //:www.didiglobal.com/passenger/create ,小程序只能用 https://:www.didiglobal.com/passenger/create
  • 打开一个新页面时链接不统一,例如打开发单页时,Web 端是 //:www.didiglobal.com/passenger/create,小程序是 /page/create
  • 页面之间跳转时,传参不统一
  • debug 成本高,修改完代码之后两端需要测试
  • 两端界面效果不一致,基础内置组件统一性建设不足
  • 工程化建设落后,例如不支持 liveroload、数据 mock、资源定位、proxy、多端统一预览
  • 接口设计不完整,生命周期、组件分层、本地 API 设计等
  • 模板 DSL 语法不规范

成长期:从伪统一到大一统

在 MPV 的实践积累下,有了一定的底气和把握,后续的规划更加明确。2018 年 4 月我们把跨端项目规模进一步扩大,想要做一个真正跨 N 端的解决方案,目标是提供标准的 MVVM 架构开发模式统一各类终端。这就是 Chameleon 的出现契机。

Chameleon 真正想要一套代码运行多端,总结下来要解决几大问题:

  • 要全面完成端开发的所有细节的统一性,特别是界面统一性有大量细节要做
  • 要在完成上一条的前提下考虑差异化定制空间
  • 持续可维护

clipboard.png

目标理想业务形态是这样的:

clipboard.png

图中上半部分是传统开发方式,下半部分 Chameleon 的模式抽象出了 UI 渲染层和本地接口能力层,业务代码一部分简单页面由 XEditor(h5Editor 的前身)编辑工具产出,另一部分工程师使用 Chameleon 开发,不止解决跨端问题,还弥补改进了工程开发过程中的效率、质量、性能与稳定性问题,让工程师专注有意义的业务,成长更快。

首个 Native 渲染引擎选择——小程序架构、RN/Weex 架构

从 MPV 到 Chameleon,外界看来最明显的变化是从跨 2 端(Web、小程序)升级到跨多端(Web、小程序、Android、iOS),最开始纠结于首个端上版本的渲染引擎使用小程序架构还是 RN/Weex 架构。

RN/Weex 网上有大量资料可查,但是小程序方面则不然。千辛万苦搜索之后,根据一位知道内情的朋友的描述分享,才有了一定的了解。

 
clipboard.png

这里分享几个印象深刻的要点:

  • 小程序展现层使用 Webview,里面内置了一套 JS 框架用来和 Native 通信,真正业务代码执行在单独 JS 虚拟机容器实例中
  • JS 虚拟机容器使用情况,iOS 系统是 JavaScriptCore,Android 系统使用 QQ 浏览器的 X5 内核
  • 小程序的各个 TAG 组件使用的数据驱动用的是 Web Components
  • 显而易见,部分性能要求较高的使用原生控件(视频、键盘等等)插入到 Webview 里面。
  • 原生控件的具体位置 Native 怎么获取?答案是由嵌入到 Webview 的一套小程序框架通知给原生层
  • 原生控件怎么保证在内部可滚动的元素(Scroll-view)里面正常滚动?答案是 CSS 设置 -webkit-over-scroll:touch 时,iOS 的实现是原生的 UIScrollView,Native 可以通过一些黑科技找到视图层级中的 UIScrollView,然后对原生控件进行插入和处理;而 Android 直接绘制没办法做到这点。现在(截至 4 月)仅仅是直接覆盖到 Webview 最外层的 scrollview 上,由内置到 Webview 的一套 JS 框架控制原生控件位置

最终多方面分析如下:

clipboard.png

虽然小程序方案看起来很简单,但其实很多细节点需要大量打磨,从确认方案到真正可以跑起来可以线上发布,仅仅花费在终端上的研发人力为 20P*6 个月,微信小程序团队的目标和我们跨端目标不一样,他们投入这么多成本是值得的,我们为了跨端没必要投入这么高成本。

所以我们选择放弃小程序渲染方案,而使用已开源的 RN/Weex 方案

第一个版本最终使用 Weex,包括团队同学去看了 Weex 源码实现。

在整体设计上仅仅使用 Weex 渲染功能,外层包装接口,保障后续能有更高扩展性。

Chameleon Native SDK

针对 Native SDK 我们主要从原生能力扩展、性能与稳定等三个方面做了工作。 

clipboard.png

  • 原生能力扩展:无论是 Webview 还是 React Native、Weex 甚至 Flutter 都只提供渲染能力(以及一些最基础本地接口),更多完成业务功能所需本地环境的能力(例如分享到微信)需要 Android 和 iOS 的 Native 往容器去扩展。本地能力包含 2 种,涉及 UI 界面的统一叫组件(UI 组件如登录、支付),涉及到纯能力调用的统一叫 API(网络、存储等)
  • 性能:界面展现和交互耗时关键取决于 2 块,资源加载耗时(非打包到安装包部分代码)、执行耗时
  • 稳定:主要关注灰度发布(风险可控)和线上止损,主要工作是按用户灰度发布、可以快速降级到 H5

以下是性能方向中的首屏加载时间的优化数据,原有 H5 使用 SSR(Server Side Render)已经算是最快的 Web 首屏技术方案了(不考虑优化后端多模块耗时的 BIGPIPE),它保持在 1.5 秒以下,在优化后降到 0.5 秒左右。  

clipboard.png

 

性能优化中我们有一个关于执行速度的 TODO 计划。同样是跨端,Flutter 之所以比 Weex 和 RN 执行速度快,主要原因是前者是编译型,客户端机器运行前已经是 CPU 可识别的机器码;后者是解释型,到客户端运行前是字符串,边编译边执行,虽然做了 JIT 尽量优化,差距还是较大。其实在这中间还有一个抹平了不同 CPU 架构下机器码差异的中间码;当然前提是开发语言改成静态类型,这里不作展开。

原本分 5 次开发的 Web 端、支付宝小程序、快应用、微信小程序、Native 端变成了 1.2 次左右开发了。最重要的是随着业务级别各端差异化的多态组件和跨端组件积累,后续 1.2 工作量也会变成 0.8,0.4 的优化主要来自两个方面:

  • 0.2 是普通跨端组件的积累,复用度变高
  • 0.2 是各类业务级别的差异化多态组件,例如登录功能,在 Web端、Native 端和小程序端实现和交互是不一致的,这时候业务形态不一样,设计的 <passport> 组件也不一样,只能各业务线去封装。

介绍一下接下来的 roadmap。

我们的最终目标是提供标准的 MVVM 架构开发模式统一各类终端。

clipboard.png

接下来的具体 roadmap 如下表所示:

clipboard.png

欢迎有共同愿景的同学加入我们一起共建,往仓库贡献自己的代码。

项目地址:https://github.com/didi/chame...

QQ 群:

clipboard.png

公众号:

clipboard.png

采访嘉宾介绍

张楠,Chameleon 创始人,技术团队负责人,前百度资深工程师,终身学习者。

查看原文

赞 20 收藏 10 评论 0

linshuai 评论了文章 · 2018-12-05

vue-manage-system 后台管理系统开发总结

前言

vue-manage-system,一个基于 Vue.js 和 element-ui 的后台管理系统模板,从2016年年底第一个commit,到现在差不多两年了,GitHub上也有了 8.6k star,也是这些让我有了持续更新的动力,其中也踩了很多坑,在这总结一下。

github地址:vue-manage-system

线上地址:https://lin-xin.gitee.io/example/work/

wms1.png

wms2.png

自定义图标

element-ui 自带的字体图标比较少,而且许多比较常见的都没有,因此需要自己引入自己想要的字体图标。最受欢迎的图标库 Font Awesome,足足有 675 个图标,但也因此导致字体文件比较大,而项目中又不需要用到这么多图标。那么这时候,阿里图标库就是一个非常不错的选择。

首先在阿里图标上创建一个项目,设置图标前缀,比如 el-icon-lx,设置Font Family,比如 lx-iconfont,添加需要用到的图标到项目中,我这边选择 Font class 生成在线链接,因为所有页面都需要用到图标,就直接在 index.html 中引入该css链接就行了

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>vue-manage-system</title>
    <!-- 这里引入阿里图标样式 -->
    <link rel="stylesheet" href="//at.alicdn.com/t/font_830376_qzecyukz0s.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

然后需要设置前缀为 el-icon-lx 的图标类名使用 lx-iconfont 字体。

[class*="el-icon-lx"], [class^=el-icon-lx] {
    font-family: lx-iconfont!important;
}

但是这个样式要放在哪里才可以呢?这可不是随便放就行的。在 main.js 中,引入了 element-ui 的样式,而样式中有这样的一段css:

[class*=" el-icon-"], [class^=el-icon-]{
    font-family: element-icons!important;
    speak: none;
    font-style: normal;
    font-weight: 400;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    vertical-align: baseline;
    display: inline-block;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

很明显,如果这段 css 在我们自定义样式后面才执行,就会覆盖了我们的样式,那自定义的图标就显示不了。而在 build 项目的时候,会把 APP.vue 中的的样式打包进 app.css 中,然后再把 main.js 中引用到的样式追加到后面。那么我们可以把自定义样式放到一个css文件中,然后在 main.js 引入 element-ui css 的后面引入,那就可以覆盖掉默认字体了,然后便可以在项目中通过 <i class="el-icon-lx-people"></i> 使用图标了。

那机智的人就发现了,我自定义图标的前缀不要含 el-icon- 就不会有这样的问题了。是的,那么为了和原有字体保持一样的样式,需要复制它的整段css

/* 假设前缀为 el-lx */
[class*="el-lx-"], [class^=el-lx-]{
    font-family: lx-iconfont!important;
    speak: none;
    font-style: normal;
    font-weight: 400;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    vertical-align: baseline;
    display: inline-block;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

导航菜单

element-ui 关于导航菜单的文档也是非常详细了,但是还是有人提 issue 或者加 QQ 问我:三级菜单怎么弄等等。而且具体的菜单项可能是服务器端根据权限而返回特定的数据项,因此不能写死在模板中。

首先定好菜单数据的格式如下,即使服务器端返回的格式不是这样,也需要前端处理成下面的格式:

export default {
    data() {
        return {
            items: [{
                icon: 'el-icon-lx-home',
                index: 'dashboard',
                title: '系统首页'
            },{
                icon: 'el-icon-lx-calendar',
                index: '1',
                title: '表单相关',
                subs: [{
                    index: '1-1',
                    title: '三级菜单',
                    subs: [{
                        index: 'editor',
                        title: '富文本编辑器'
                    }]
                }]
            },{
                icon: 'el-icon-lx-warn',
                index: '2',
                title: '错误处理',
                subs: [{
                    index: '404',
                    title: '404页面'
                }]
            }]
        }
    }
}

icon 就是菜单图标,就可以用到我们上面自定义的图标了;index 就是路由地址;title 就是菜单名称;subs 就是子菜单了。而模板则通过判断菜单中是否包含 subs 从而显示二级菜单和三级菜单。

<el-menu :default-active="onRoutes" :collapse="collapse" router>
    <template v-for="item in items">
        <template v-if="item.subs">
            <el-submenu :index="item.index" :key="item.index">
                <template slot="title">
                    <i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
                </template>
                <template v-for="subItem in item.subs">
                    <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
                        <template slot="title">{{ subItem.title }}</template>
                        <!-- 三级菜单 -->
                        <el-menu-item v-for="(threeItem,i) in subItem.subs" :key="i" :index="threeItem.index">
                            {{ threeItem.title }}
                        </el-menu-item>
                    </el-submenu>
                    <el-menu-item v-else :index="subItem.index" :key="subItem.index">
                        {{ subItem.title }}
                    </el-menu-item>
                </template>
            </el-submenu>
        </template>
        <!-- 没有二级菜单 -->
        <template v-else>
            <el-menu-item :index="item.index" :key="item.index">
                <i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
            </el-menu-item>
        </template>
    </template>
</el-menu>

这样就完成了一个动态的导航菜单。

通过 Header 组件中的一个按钮来触发 Sidebar 组件展开或收起,涉及到了组件之间传递数据,这里通过 Vue.js 单独的事件中心(Event Bus)管理组件间的通信。

const bus = new Vue();

在 Header 组件中点击按钮时触发 collapse 事件:

bus.$emit('collapse', true);

在 Sidebar 组件中监听 collapse 事件:

bus.$on('collapse', msg => {
    this.collapse = msg;
})

图表自适应

vue-manage-system 中用到的图表插件是 vue-schart,是把一个基于 canvas 的图表插件 schart.js 进行了封装。要做到图表能够自适应宽度,随着 window 或者父元素的大小改变而重新渲染,如果图表插件里没实现该功能,就需要自己手动实现。

vue-schart 中提供了 renderChart() 的方法可以重新渲染图表,Vue.js 中父组件调用子组件的方法,可以通过 $refs 进行调用。

<schart ref="bar" canvasId="bar" :data="data" type="bar" :options="options"></schart>

然后监听 window 的 resize 事件,调用 renderChart() 方法重新渲染图表。

import Schart from 'vue-schart';
export default {
    components: {
        Schart
    },
    mounted(){
        window.addEventListener('resize', ()=>{
            this.$refs.bar.renderChart();
        })
    }
}

不过也要记得组件销毁时移除监听哦!监听窗口大小改变完成了,那父元素大小改变呢?因为父元素宽度设为百分比,当侧边栏折叠的时候,父元素的宽度发生了变化。但是 div 并没有 resize 事件,无法监听到它的宽度改变,但是触发折叠的时候,我们是知道的。那么是否可以通过监听到折叠变化的时候,再调用渲染函数重新渲染图表呢?那么还是通过 Event Bus 监听侧边栏的改变,并在 300ms 后重新渲染,因为折叠时候有 300ms 的动画过程

bus.$on('collapse', msg => {
    setTimeout(() => {
        this.$refs.bar.renderChart();
    }, 300);
});

多标签页

多标签页,也是提 issue 最多的一个功能。

当在 A 标签页输入一些内容之后,打开 B 标签再返回到 A,要保留离开前的状态,因此需要使用 keep-alive 进行缓存,而且关闭之后的标签页就不再缓存,避免关闭后再打开还是之前的状态。keep-alive 的属性 include 的作用就是只有匹配的组件会被缓存。include 匹配的不是路由名,而是组件名,那么每个组件都需要添加 name 属性。

在 Tags 组件中,监听路由变化,将打开的路由添加到标签页中:

export default {
    data() {
        return {
            tagsList: []
        }
    },
    methods: {
        setTags(route){
            const isExist = this.tagsList.some(item => {
                return item.path === route.fullPath;
            })
            if(!isExist){
                this.tagsList.push({
                    title: route.meta.title,
                    path: route.fullPath,
                    name: route.matched[1].components.default.name
                })
            }
        }
    },
    watch:{
        $route(newValue, oldValue){
            this.setTags(newValue);
        }
    }
}

在 setTags 方法中,将一个标签对象存到标签数组中,包括title(标签显示的title),path(标签的路由地址),name(组件名,用于include匹配的)。路由地址需要用 fullPath 字段,如果使用 path 字段,那如果地址后面带有参数,就都没保存起来了。

在 Home 组件中,监听到标签的变化,缓存需要的组件。

<keep-alive :include="tagsList">
    <router-view></router-view>
</keep-alive>
export default {
    data(){
        return {
            tagsList: []
        }
    },
    created(){
        // 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。
        bus.$on('tags', msg => {
            let arr = [];
            for(let i = 0, len = msg.length; i < len; i ++){
                // 提取组件名存到tagsList中,通过include匹配
                msg[i].name && arr.push(msg[i].name);
            }
            this.tagsList = arr;
        })
    }
}

总结

由于该项目中不包含任何业务代码,所以还是相对比较简单的,不过从开发中还是积累了一些经验,在其它项目中可以更加熟练地开发。功能虽然不算多,但是也勉强够用,如果有什么好的建议,可以开 issue 一起讨论。

更多文章:lin-xin/blog

查看原文

linshuai 关注了专栏 · 2018-12-04

蚂蚁技术

蚂蚁金服科技官方账号,专注于分享蚂蚁金服的技术

关注 2045

linshuai 赞了文章 · 2018-12-04

吐槽:团队解散,我们该何去何从?

面对裁员,我们能做些什么?小程序项目如何设置资源的防盗链?听说京东都裁员了,你还敢说自己学不动吗?小程序项目如何设置资源的防盗链?从入门到真实项目配置,首席填坑官∙苏南的专栏

  • 写在最前:纯属吐槽,随笔,勿喷!

就在前些天,下班回家的路上,看到群信息,说:听说京东裁员了~

图片来源于掘金,码农书籍,一起阅读,一起进步,前端开发,欠费通知,小程序的访问量,从入门到真实项目配置,首席填坑官∙苏南的专栏

也是在上上月,也一度被传的沸沸扬扬的:阿里京东华为相继被曝停止社招,新闻也是满天飞舞,不管是裁员,还是停止社招,这些事情没有落在亲身经历,没有落在自己身上我们都会觉得不痛不痒,毕竟一个旁观者,永远无法感受当事人的喜怒哀乐~。

俗话说:人无远虑 必有近忧,假如当你遇上裁员,又该如何面对~

今天看到这个新闻,第一时间想到的是自己,当然啦,大家别慌,我没有被裁,不用紧张,

工作几年下来相信大家或多或少遇到过一些要么自己、要么身边的朋友、同事,都有过一些欠薪、公司倒闭、团队解散甚至裁员的事。

嗯,还有刚刚过去的掘金

嗯,还有刚刚过去的掘金,码农书籍,一起阅读,一起进步,前端开发,欠费通知,小程序的访问量,从入门到真实项目配置,首席填坑官∙苏南的专栏

往事不堪回首

为什么说想到自己呢?我没有被裁过,但团队解散过,这个事情得从去年说起:

从毕业到现在,工作也有些年头了,这些年多多少少也遇到过一些机会,

  • 2014年中旬,一个阿里PHP大佬,自己创业,做婚庆的电子请柬,记得那个时候特别流行H5移动微场景制作,某企秀、兔*,之类的公司应风而起,包括到现在好像也还做的不错,当时是兼职业余时间帮做一些前端工作,一直持维了近一年时间合作的都很不错,后面更是亲自邀请我让去他公司玩一下,来回车票都包销(公司在湖南),再后来提出让全职去他公司(合伙性质),基础保底年薪是20W,但最近因为不在深圳、觉得项目并不是特别看好等原因放弃了~
  • 15年左右,当时是在个游戏公司,有个领导出去创业,当时拉了几个团队的同事,其中也有我,是的,结果你已经猜到了,我最后没有去,主要受当时所在的游戏公司部门的产品经理特别变态、真的特别变态,天天加班到12点,早上晚到一会,开会点名批评(公司是有这个福利的,加班晚第二天能晚到),说我们部门不能搞特殊(到底谁在搞特殊??),要按时上班,当时一度特别烦躁,想着去了创业公司……。
  • …… 中途也有小米公司,某部门新组件的研发负责人的邀请,
  • 这些都是后话了,就在去年、对去年,今天都还清楚的记得2018-07-26,这天去了前面提到的解散的公司报到,当时也是之前某公司的领导找到我,说他将出任某公司研发总监职位,新成立的子公司,问我是否有兴趣一起,在考虑过综合因素后最后决定去了,主要是公司也在深圳、薪资是有涨幅的,更重要的是了解了一下母公司的背景(挺强、有干爹的那种`)资金方面不会出问题,钱都不是问题了,未来途还是问题吗?

回头想想这些年,因为种种原因,错过了很多机遇,随着年龄的增涨,是时候博一下了(哪怕是失败,自己也并不会损失什么),跟几个不错的朋友也讨论过后,也认为觉得一次不错的机会,所以就欣然前往了,

公司是从0开始的,

一切都是从0开始的,

包括第一行代码都是从0开始写的,每一天都是从早到晚的在忙,赶着把产品上线,几乎每天都在加班,但团队的每个同事们都毫无怨言,因为这些都是在已知的预料之中。

日复一日,产品迭代了又迭代、APP、PC端、小程序等都陆续上线,慢慢走上正轨,业务也开始接入,团队成员都感觉看到了胜利的曙光。

然而,人在家中坐、锅从天人上来,暴风雨来临的总是显得特别的宁静,在3月初中旬左右(往事不堪回首~),周末还约了同事一起穿越了南澳东西涌

东西涌有毒,回来公司就没了~,码农书籍,一起阅读,一起进步,前端开发,欠费通知,小程序的访问量,从入门到真实项目配置,首席填坑官∙苏南的专栏
新一周周一,一如往常的去公司上班,刚坐来,准备大显身手,开始元气满满的一天,领导发来消息让去他办公室一趟,后面说了些公司的情况:

  • 大概意思是总部对于子公司业务、管理层的不满,做采取一些措施,但这个不会影响到下面员工等等之类的,他可能也会有所变动等,提前跟我说一下。
  • 然后又过了没有几天,领导叫去了全部leader,说总部出于某些原因,子公司可能不会再投资了~~,是不是很棒棒??晴天霹雷~、刺激不??第一反应是先来让几个深呼吸、再呼吸,然后轻轻的扶住墙、嗯,扶住墙~

上右图为公司一同事当时的感慨~

事情已经这样了,后面也就不用再继续讲了,我没有遇到过裁员,但我经历了团队解散,是不是很优秀??

看到这里,可能有些同学会认为我的经历的些扯了,合伙是假,压榨、利用、……等是真的;但我想说:如果你遇到这样的事情,应该值得高兴,至少还有被压榨、利用的价值,换个角度讲,凡事没有绝对的公平,任何事如果没有一定的利益关系捆绑,都难以长期维持,所以不存在利用一说。

至少我觉得自己很棒,能得到大家的认可。

如果你连利用的价值都没有,那么恭喜你,裁员标签,可能就要落在你身上了,

前路在何方?

说了这么多,其实是想表达不管是裁员、欠薪、还是团队解散,当这样的问题发生时?我们该怎么做、怎么做、怎么做呢??
我只能说送上四个字:## 未雨绸缪 ##

  • 首先,好好跟公司沟通,维护好自己应有的权益,哪怕仲裁,这点不可妥协~;
  • 其次 ,不要因为这种小意外,而否定了自己能力,自信的人最美(你永远是最棒的),此处不留爷 自有留爷处。
  • 然后 好好静下心来,好好复习准备下一份工作,只要你活好,走到哪里都不怕,条条大路通罗马,你的星光大道就在前方;
  • 最后,也是最重要的,工作之余,我们也应该多关注一些技术的发展趋势,不断的学习,强化自己的专业技能,才能不会被时代的洪水淹没。
  • 特别是近两年,互联网一直处理一个高速持续的过程发展,前端更是如此:ReactVueAngular等,甚至还有衍生出的 React Navite 、H5的新特性dialogshowModal()等等,常常会有网友在调侃老了、学不动了,调侃的同时却又阐述着无奈,迫于生活的压力,不得不提速前行。

​未来的路我们是无法预知的,谁都不知道明天将会发生什么,我们能做的就是未雨绸缪,在事情未发生之前,自己多做、多想、多学。

一入IT深似海,从此妹子是路人,最近不是React、Vue都发布了新版本吗?好好学吧🤪。

以上来自突然的一些想法就吐槽一下(勿喷),给自己一点危机感,不管裁员/团队解散是否属实我们都努力前行,加油,你是最棒的。

欢迎关注公号:IT平头哥联盟
一起学习进步

用心分享 做有温度的攻城狮,苏南的专栏

更多文章:

作者:苏南 - 首席填坑官
链接:http://susouth.com/
交流:912594095、公众号:honeyBadger8
本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。
查看原文

赞 11 收藏 3 评论 19

linshuai 评论了文章 · 2018-11-23

GitHub上最热门的十大Vue.js项目

在过去的一个月里,Mybridge从将近150个Vue.js开源项目中精选出十个热门项目,旨在帮助开发者找到自己需要的Vue.js开源项目

上榜开源项目所获得Star数平均为:268

涉及的领域包括:系统设计,移动组件,图像过滤器,图表,Nuxtjs,组件,录音机,表格,谷歌地图,悬停效果

下面就是上榜的开源项目详情:

1.Vue-argon-design-system

https://github.com/creativeti... Star 164

Vue-argon-design-system是基于Design System for Bootstrap 4开发,它由100多个独立的组件构成,用户可以自由选择和组合,所有组件都可以采用颜色变化,你可以使用SASS文件轻松修改。

2.Vue-ydui

https://github.com/ydcss/vue-... Star 1974

Vue-ydui 是 YDUI Touch 的一个Vue2.x实现版本,专为移动端打造,在追求完美视觉体验的同时也保证了其性能高效。在开始使用 vue-ydui 之前,有必要先了解 Vue 的相关基础知识以及Vue 组件,并了解移动端相关特性。

3.Imagvue

https://github.com/runkids/Im... Star 210

Imagvue是Vue.js的图像组件,具有以下特性:

● Imagvue 提供基本的图像处理道具(大小,模糊,对比度,灰度等)。

● 支持图像延迟加载。

● 所有属性都可以与数据绑定

4.Vue-apexcharts

https://github.com/apexcharts... Star 101

ApexCharts的Vue.js组件,用Vue.js对ApexCharts进行封装,实现在vue中构建交互式可视化

5.Nuxpress

https://github.com/galvez/nux... Star 90

一个基于Nuxt的博客引擎和样板

6.Vui

https://github.com/vui-kit/vui Star 47

一个基于VUE的简易框架,用于Vue2单页面应用程序的轻量级组件库,此项目的创建主要是为了深入学习vue

7.Vue-audio-recorder

https://github.com/grishkovel... Star 70

适用于VueJS应用程序的简易录音机,它允许在服务器上创建,播放,下载和存储记录。

8.Vue-genesis-forms

https://github.com/BlackMix/v... Star 50

Vue-genesis-forms可以帮助你在Vue.js中轻松创建表单

9.Vue2-gmap-custom-marker

https://github.com/eregnier/v... Star 27

该组件允许用户使用Overlay在地图上显示自定义html内容。这个组件是google map V3 overlay code的改编,其中一些创意来自angularjs谷歌地图

10.Vue-good-links

https://github.com/xaksis/vue... Star 60

给你的超链接添加一个漂亮动画的vue插件,如翻转、跳跃、滚动......

原文链接:https://www.jianshu.com/write...

查看原文