webpack 引入dsbridge_引入webpack - CSDN
精华内容
参与话题
  • 作者:mcuking(杭州个推) ... 笔者在公司用 web 技术开发移动端应用已经有一年多的时间了,开始主要以 vue 技术栈配合 native 为主,目前演进成 vue + react native 技术架构,vue 主要负责开发 OA 业务,比如报销...

    作者:mcuking(杭州个推)

    https://juejin.im/post/5d759f706fb9a06afa32adec

    笔者在公司用 web 技术开发移动端应用已经有一年多的时间了,开始主要以 vue 技术栈配合 native 为主,目前演进成 vue + react native 技术架构,vue 主要负责开发 OA 业务,比如报销、出差、crm 等等,react native 主要负责即时通信部分,是在 mattermost-mobile[1] 的基础上修改的(mattermost 是一个开源的即时通讯方案)。

    因为公司在这方面没有太多技术沉淀,所以在开发期间遇到了很多坑,经过一年多的技术攻克积累,最终形成了这套比较完善的解决方案,总结出来希望能够帮助到大家,尤其是对一些中小公司这方面经验不足的(PS: 大公司估计有他们自己的一套方案了)。

    好了废话不多说,先亮下这个库的 GitHub 地址,后面还会不断完善,欢迎 star:

    mobile-web-best-practice[2]

    移动端 web 最佳实践,基于 vue-cli3[3] 搭建的 typescript[4] 项目,可以用于 hybrid 应用或者纯 webapp 开发。以下大部分内容同样适用于 react[5] 等前端框架。

    其中有三个点尚在完善中:领域驱动设计(DDD)应用、微前端、性能监控,后续完成后会以单独的文章发出来。其中性能监控还没有太好的选择,类似错误监控 sentry 那种开源免费而且功能强大的工具,如果有人知道的麻烦告知下。文中难免有些错误或者更好的方案,也欢迎不吝赐教。

    目录

    • 组件库[6]

    • JSBridge[7]

    • 路由堆栈管理(模拟原生 APP 导航)[8]

    • 请求数据缓存[9]

    • 构建时预渲染[10]

    • Webpack 策略[11]

      • 基础库抽离[12]

    • 手势库[13]

    • 样式适配[14]

    • 表单校验[15]

    • 阻止原生返回事件[16]

    • 通过 UA 获取设备信息[17]

    • mock 数据[18]

    • 调试控制台[19]

    • 抓包工具[20]

    • 异常监控平台[21]

    • 常见问题[22]

    组件库

    vant[23]

    vux[24]

    mint-ui[25]

    cube-ui[26]

    vue 移动端组件库目前主要就是上面罗列的这几个库,本项目使用的是有赞前端团队开源的 vant。

    vant 官方目前已经支持自定义样式主题,基本原理就是在 less-loader[27] 编译 less[28] 文件到 css 文件过程中,利用 less 提供的 modifyVars[29] 对 less 变量进行修改,本项目也采用了该方式,具体配置请查看相关文档:

    定制主题[30]

    推荐一篇介绍各个组件库特点的文章:

    Vue 常用组件库的比较分析(移动端)[31]

    JSBridge

    DSBridge-IOS[32]

    DSBridge-Android[33]

    WebViewJavascriptBridge[34]

    混合应用中一般都是通过 webview 加载网页,而当网页要获取设备能力(例如调用摄像头、本地日历等)或者 native 需要调用网页里的方法,就需要通过 JSBridge 进行通信。

    开源社区中有很多功能强大的 JSBridge,例如上面列举的库。本项目基于保持 iOS android 平台接口统一原因,采用了 DSBridge,各位可以选择适合自己项目的工具。

    本项目以 h5 调用 native 提供的同步日历接口为例,演示如何在 dsbridge 基础上进行两端通信的。下面是两端的关键代码摘要:

    安卓端同步日历核心代码,具体代码请查看与本项目配套的安卓项目 mobile-web-best-practice-container[35]:

    public class JsApi {
        /**
         * 同步日历接口
         * msg 格式如下:
         * ...
         */
        @JavascriptInterface
        public void syncCalendar(Object msg, CompletionHandler<Integer> handler) {
            try {
                JSONObject obj = new JSONObject(msg.toString());
                String id = obj.getString("id");
                String title = obj.getString("title");
                String location = obj.getString("location");
                long startTime = obj.getLong("startTime");
                long endTime = obj.getLong("endTime");
                JSONArray earlyRemindTime = obj.getJSONArray("alarm");
                String res = CalendarReminderUtils.addCalendarEvent(id, title, location, startTime, endTime, earlyRemindTime);
                handler.complete(Integer.valueOf(res));
            } catch (Exception e) {
                e.printStackTrace();
                handler.complete(6005);
            }
        }
    }
    

    h5 端同步日历核心代码(通过装饰器来限制调用接口的平台)

    class NativeMethods {
      // 同步到日历
      @p()
      public syncCalendar(params: SyncCalendarParams) {
        const cb = (errCode: number) => {
          const msg = NATIVE_ERROR_CODE_MAP[errCode];
    
          Vue.prototype.$toast(msg);
    
          if (errCode !== 6000) {
            this.errorReport(msg, 'syncCalendar', params);
          }
        };
        dsbridge.call('syncCalendar', params, cb);
      }
    
      // 调用 native 接口出错向 sentry 发送错误信息
      private errorReport(errorMsg: string, methodName: string, params: any) {
        if (window.$sentry) {
          const errorInfo: NativeApiErrorInfo = {
            error: new Error(errorMsg),
            type: 'callNative',
            methodName,
            params: JSON.stringify(params)
          };
          window.$sentry.log(errorInfo);
        }
      }
    }
    
    /**
     * @param {platforms} - 接口限制的平台
     * @return {Function} - 装饰器
     */
    function p(platforms = ['android', 'ios']) {
      return (target: AnyObject, name: string, descriptor: PropertyDescriptor) => {
        if (!platforms.includes(window.$platform)) {
          descriptor.value = () => {
            return Vue.prototype.$toast(
              `当前处在 ${window.$platform} 环境,无法调用接口哦`
            );
          };
        }
    
        return descriptor;
      };
    }
    

    另外推荐一个笔者之前写的一个基于安卓平台实现的教学版 JSBridge[36],里面详细阐述了如何基于底层接口一步步封装一个可用的 JSBridge:

    JSBridge 实现原理[37]

    路由堆栈管理(模拟原生 APP 导航)

    vue-page-stack[38]

    vue-navigation[39]

    vue-stack-router[40]

    在使用 h5 开发 app,会经常遇到下面的需求:从列表进入详情页,返回后能够记住当前位置,或者从表单点击某项进入到其他页面选择,然后回到表单页,需要记住之前表单填写的数据。可是目前 vue 或 react 框架的路由,均不支持同时存在两个页面实例,所以需要路由堆栈进行管理。

    其中 vue-page-stack 和 vue-navigation 均受 vue 的 keepalive 启发,基于 vue-router[41],当进入某个页面时,会查看当前页面是否有缓存,有缓存的话就取出缓存,并且清除排在他后面的所有 vnode,没有缓存就是新的页面,需要存储或者是 replace 当前页面,向栈里面 push 对应的 vnode,从而实现记住页面状态的功能。

    而逻辑思维前端团队的 vue-stack-router 则另辟蹊径,抛开了 vue-router,自己独立实现了路由管理,相较于 vue-router,主要是支持同时可以存活 A 和 B 两个页面的实例,或者 A 页面不同状态的两个实例,并支持原生左滑功能。但由于项目还在初期完善,功能还没有 vue-router 强大,建议持续关注后续动态再做决定是否引入。

    本项目使用的是 vue-page-stack,各位可以选择适合自己项目的工具。同时推荐几篇相关文章:

    【vue-page-stack】Vue 单页应用导航管理器 正式发布[42]

    Vue 社区的路由解决方案:vue-stack-router[43]

    请求数据缓存

    mem[44]

    在我们的应用中,会存在一些很少改动的数据,而这些数据有需要从后端获取,比如公司人员、公司职位分类等,此类数据在很长一段时间时不会改变的,而每次打开页面或切换页面时,就重新向后端请求。为了能够减少不必要请求,加快页面渲染速度,可以引用 mem 缓存库。

    mem 基本原理是通过以接收的函数为 key 创建一个 WeakMap,然后再以函数参数为 key 创建一个 Map,value 就是函数的执行结果,同时将这个 Map 作为刚刚的 WeakMap 的 value 形成嵌套关系,从而实现对同一个函数不同参数进行缓存。而且支持传入 maxAge,即数据的有效期,当某个数据到达有效期后,会自动销毁,避免内存泄漏。

    选择 WeakMap 是因为其相对 Map 保持对键名所引用的对象是弱引用,即垃圾回收机制不将该引用考虑在内。只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。

    mem 作为高阶函数,可以直接接受封装好的接口请求。但是为了更加直观简便,我们可以按照类的形式集成我们的接口函数,然后就可以用装饰器的方式使用 mem 了(装饰器只能修饰类和类的类的方法,因为普通函数会存在变量提升)。下面是相关代码:

    import http from '../http';
    import mem from 'mem';
    
    /**
     * @param {MemOption} - mem 配置项
     * @return {Function} - 装饰器
     */
    export default function m(options: AnyObject) {
      return (target: AnyObject, name: string, descriptor: PropertyDescriptor) => {
        const oldValue = descriptor.value;
        descriptor.value = mem(oldValue, options);
        return descriptor;
      };
    }
    
    class Home {
      @m({ maxAge: 60 * 1000 })
      public async getUnderlingDailyList(
        query: ListQuery
      ): Promise<{ total: number; list: DailyItem[] }> {
        const {
          data: { total, list }
        } = await http({
          method: 'post',
          url: '/daily/getList',
          data: query
        });
    
        return { total, list };
      }
    }
    
    export default new Home();
    

     

    构建时预渲染

    针对目前单页面首屏渲染时间长(需要下载解析 js 文件然后渲染元素并挂载到 id 为 app 的 div 上),SEO 不友好(index.html 的 body 上实际元素只有 id 为 app 的 div 元素,真正的页面元素都是动态挂载的,搜索引擎的爬虫无法捕捉到),目前主流解决方案就是服务端渲染(SSR),即从服务端生成组装好的完整静态 html 发送到浏览器进行展示,但配置较为复杂,一般都会借助框架,比如 vue 的 nuxt.js[45],react 的 next[46]。

    其实有一种更简便的方式--构建时预渲染。顾名思义,就是项目打包构建完成后,启动一个 Web Server 来运行整个网站,再开启多个无头浏览器(例如 Puppeteer[47]、Phantomjs[48] 等无头浏览器技术)去请求项目中所有的路由,当请求的网页渲染到第一个需要预渲染的页面时(需提前配置需要预渲染页面的路由),会主动抛出一个事件,该事件由无头浏览器截获,然后将此时的页面内容生成一个 HTML(包含了 JS 生成的 DOM 结构和 CSS 样式),保存到打包文件夹中。

    根据上面的描述,我们可以其实它本质上就只是快照页面,不适合过度依赖后端接口的动态页面,比较适合变化不频繁的静态页面。

    实际项目相关工具方面比较推荐 prerender-spa-plugin[49] 这个 webpack 插件,下面是这个插件的原理图。不过有两点需要注意:

    一个是这个插件需要依赖 Puppeteer,而因为国内网络原因以及本身体积较大,经常下载失败,不过可以通过 .npmrc 文件指定 Puppeteer 的下载路径为国内镜像;

    另一个是需要设置路由模式为 history 模式(即基于 html5 提供的 history api 实现的,react 叫 BrowserRouter,vue 叫 history),因为 hash 路由无法对应到实际的物理路由。(即线上渲染时 history 下,如果 form 路由被设置成预渲染,那么访问 /form/ 路由时,会直接从服务端返回 form 文件夹下的 index.html,之前打包时就已经预先生成了完整的 HTML 文件 )

     

    本项目已经集成了 prerender-spa-plugin,但由于和 vue-stack-page/vue-navigation 这类路由堆栈管理器一起使用有问题(原因还在查找,如果知道的朋友也可以告知下),所以 prerender 功能是关闭的。

    同时推荐几篇相关文章:

    vue 预渲染之 prerender-spa-plugin 解析(一)[50]

    使用预渲提升 SPA 应用体验[51]

    Webpack 策略

    基础库抽离

    对于一些基础库,例如 vue、moment 等,属于不经常变化的静态依赖,一般需要抽离出来以提升每次构建的效率。目前主流方案有两种:

    一种是使用 webpack-dll-plugin[52] 插件,在首次构建时就讲这些静态依赖单独打包,后续只需引入早已打包好的静态依赖包即可;

    另一种就是外部扩展 Externals[53] 方式,即把不需要打包的静态资源从构建中剔除,使用 CDN 方式引入。下面是 webpack-dll-plugin 相对 Externals 的缺点:

    1. 需要配置在每次构建时都不参与编译的静态依赖,并在首次构建时为它们预编译出一份 JS 文件(后文将称其为 lib 文件),每次更新依赖需要手动进行维护,一旦增删依赖或者变更资源版本忘记更新,就会出现 Error 或者版本错误。

    2. 无法接入浏览器的新特性 script type="module",对于某些依赖库提供的原生 ES Modules 的引入方式(比如 vue 的新版引入方式)无法得到支持,没法更好地适配高版本浏览器提供的优良特性以实现更好地性能优化。

    3. 将所有资源预编译成一份文件,并将这份文件显式注入项目构建的 HTML 模板中,这样的做法,在 HTTP1 时代是被推崇的,因为那样能减少资源的请求数量,但在 HTTP2 时代如果拆成多个 CDN Link,就能够更充分地利用 HTTP2 的多路复用特性。

    不过选择 Externals 还是需要一个靠谱的 CDN 服务的。

    本项目选择的是 Externals,各位可根据项目需求选择不同的方案。

    更多内容请查看这篇文章(上面观点来自于这篇文章):

    Webpack 优化——将你的构建效率提速翻倍[54]

    手势库

    hammer.js[55]

    AlloyFinger[56]

    在移动端开发中,一般都需要支持一些手势,例如拖动(Pan),缩放(Pinch),旋转(Rotate),滑动(swipe)等。目前已经有很成熟的方案了,例如 hammer.js 和腾讯前端团队开发的 AlloyFinger 都很不错。本项目选择基于 hammer.js 进行二次封装成 vue 指令集,各位可根据项目需求选择不同的方案。

    下面是二次封装的关键代码,其中用到了 webpack 的 require.context 函数来获取特定模块的上下文,主要用来实现自动化导入模块,比较适用于像 vue 指令这种模块较多的场景:

    // 用于导入模块的上下文
    export const importAll = (
      context: __WebpackModuleApi.RequireContext,
      options: ImportAllOptions = {}
    ): AnyObject => {
      const { useDefault = true, keyTransformFunc, filterFunc } = options;
    
      let keys = context.keys();
    
      if (isFunction(filterFunc)) {
        keys = keys.filter(filterFunc);
      }
    
      return keys.reduce((acc: AnyObject, curr: string) => {
        const key = isFunction(keyTransformFunc) ? keyTransformFunc(curr) : curr;
        acc[key] = useDefault ? context(curr).default : context(curr);
        return acc;
      }, {});
    };
    
    // directives 文件夹下的 index.ts
    const directvieContext = require.context('./', false, /.ts$/);
    const directives = importAll(directvieContext, {
      filterFunc: (key: string) => key !== './index.ts',
      keyTransformFunc: (key: string) =>
        key.replace(/^.//, '').replace(/.ts$/, '')
    });
    
    export default {
      install(vue: typeof Vue): void {
        Object.keys(directives).forEach((key) =>
          vue.directive(key, directives[key])
        );
      }
    };
    
    // touch.ts
    export default {
      bind(el: HTMLElement, binding: DirectiveBinding) {
        const hammer: HammerManager = new Hammer(el);
        const touch = binding.arg as Touch;
        const listener = binding.value as HammerListener;
        const modifiers = Object.keys(binding.modifiers);
    
        switch (touch) {
          case Touch.Pan:
            const panEvent = detectPanEvent(modifiers);
            hammer.on(`pan${panEvent}`, listener);
            break;
          ...
        }
      }
    };
    

    另外推荐一篇关于 hammer.js 和一篇关于 require.context 的文章:

    H5 案例分享:JS 手势框架 —— Hammer.js[57]

    使用 require.context 实现前端工程自动化[58]

    样式适配

    postcss-px-to-viewport[59]

    Viewport Units Buggyfill[60]

    flexible[61]

    postcss-pxtorem[62]

    Autoprefixer[63]

    browserslist[64]

    在移动端网页开发时,样式适配始终是一个绕不开的问题。对此目前主流方案有 vw 和 rem(当然还有 vw + rem 结合方案,请见下方 rem-vw-layout 仓库),其实基本原理都是相通的,就是随着屏幕宽度或字体大小成正比变化。因为原理方面的详细资料网络上已经有很多了,就不在这里赘述了。下面主要提供一些这工程方面的工具。

    关于 rem,阿里无线前端团队在 15 年的时候基于 rem 推出了 flexible 方案,以及 postcss 提供的自动转换 px 到 rem 的插件 postcss-pxtorem。

    关于 vw,可以使用 postcss-px-to-viewport 进行自动转换 px 到 vw。postcss-px-to-viewport 相关配置如下:

    "postcss-px-to-viewport": {
      viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是375
      viewportHeight: 667, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
      unitPrecision: 3,  // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
      viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
      selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
      minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
      mediaQuery: false // 媒体查询里的单位是否需要转换单位
    }
    

    下面是 vw 和 rem 的优缺点对比图:

    关于 vw 兼容性问题,目前在移动端 iOS 8 以上以及 Android 4.4 以上获得支持。如果有兼容更低版本需求的话,可以选择 viewport 的 pollify 方案,其中比较主流的是 Viewport Units Buggyfill[65]。

    本方案因不准备兼容低版本,所以直接选择了 vw 方案,各位可根据项目需求选择不同的方案。

    另外关于设置 css 兼容不同浏览器,想必大家都知道 Autoprefixer(vue-cli3 已经默认集成了),那么如何设置要兼容的范围呢?推荐使用 browserslist,可以在 .browserslistrc 或者 pacakage.json 中 browserslist 部分设置兼容浏览器范围。因为不止 Autoprefixer,还有 Babel,postcss-preset-env 等工具都会读取 browserslist 的兼容配置,这样比较容易使 js css 兼容浏览器的范围保持一致。下面是本项目的 .browserslistrc 配置:

    iOS >= 10  //  即 iOS Safari
    Android >= 6.0 // 即 Android WebView
    last 2 versions // 每个浏览器最近的两个版本
    

    最后推荐一些移动端样式适配的资料:

    rem-vw-layout[66]

    细说移动端 经典的 REM 布局 与 新秀 VW 布局[67]

    如何在 Vue 项目中使用 vw 实现移动端适配[68]

    表单校验

    async-validator[69]

    vee-validate[70]

    由于大部分移动端组件库都不提供表单校验,因此需要自己封装。目前比较多的方式就是基于 async-validator 进行二次封装(elementUI 组件库提供的表单校验也是基于 async-validator ),或者使用 vee-validate(一种基于 vue 模板的轻量级校验框架)进行校验,各位可根据项目需求选择不同的方案。

    本项目的表单校验方案是在 async-validator 基础上进行二次封装,代码如下,原理很简单,基本满足需求。如果还有更完善的方案,欢迎提出来。

    其中 setRules 方法是将组件中设置的 rules(符合 async-validator 约定的校验规则)按照需要校验的数据的名字为 key 转化一个对象 validator,value 是 async-validator 生成的实例。validator 方法可以接收单个或多个需要校验的数据的 key,然后就会在 setRules 生成的对象 validator 中寻找 key 对应的 async-validator 实例,最后调用实例的校验方法。当然也可以不接受参数,那么就会校验所有传入的数据。

    import schema from 'async-validator';
    ...
    
    class ValidatorUtils {
      private data: AnyObject;
      private validators: AnyObject;
    
      constructor({ rules = {}, data = {}, cover = true }) {
        this.validators = {};
        this.data = data;
        this.setRules(rules, cover);
      }
    
      /**
       * 设置校验规则
       * @param rules async-validator 的校验规则
       * @param cover 是否替换旧规则
       */
      public setRules(rules: ValidateRules, cover: boolean) {
        if (cover) {
          this.validators = {};
        }
    
        Object.keys(rules).forEach((key) => {
          this.validators[key] = new schema({ [key]: rules[key] });
        });
      }
    
      public validate(
        dataKey?: string | string[]
      ): Promise<ValidateError[] | string | string[] | undefined> {
        // 错误数组
        const err: ValidateError[] = [];
    
        Object.keys(this.validators)
          .filter((key) => {
            // 若不传 dataKey 则校验全部。否则校验 dataKey 对应的数据(dataKey 可以对应一个(字符串)或多个(数组))
            return (
              !dataKey ||
              (dataKey &&
                ((_.isString(dataKey) && dataKey === key) ||
                  (_.isArray(dataKey) && dataKey.includes(key))))
            );
          })
          .forEach((key) => {
            this.validators[key].validate(
              { [key]: this.data[key] },
              (error: ValidateError[]) => {
                if (error) {
                  err.push(error[0]);
                }
              }
            );
          });
    
        if (err.length > 0) {
          return Promise.reject(err);
        } else {
          return Promise.resolve(dataKey);
        }
      }
    }
    

    阻止原生返回事件

    开发中可能会遇到下面这个需求:当页面弹出一个 popup 或 dialog 组件时,点击返回键时是隐藏弹出的组件而不是返回到上一个页面。

    为了解决这个问题,我们可以从路由栈角度思考。一般弹出组件是不会在路由栈上添加任何记录,因此我们在弹出组件时,可以在路由栈中 push 一个记录,为了不让页面跳转,我们可以把跳转的目标路由设置为当前页面路由,并加上一个 query 来标记这个组件弹出的状态。

    然后监听 query 的变化,当点击弹出组件时,query 中与该弹出组件有关的标记变为 true,则将弹出组件设为显示;当用户点击 native 返回键时,路由返回上一个记录,仍然是当前页面路由,不过 query 中与该弹出组件有关的标记不再是 true 了,这样我们就可以把弹出组件设置成隐藏,同时不会返回上一个页面。相关代码如下:

    <template>
      <van-cell title="几时入坑"
                        is-link
                        :value="textData.pitDateStr"
                        @click="goToSelect('calendar')" />
      <van-popup v-model="showCalendar"
                  position="right"
                  :style="{ height: '100%', width: '100%' }">
        <Calendar title="选择入坑时间"
                  @select="onSelectPitDate" />
      </van-popup>
    <template/>
    <script lang="ts">
    ...
    export default class Form extends Vue {
      private showCalendar = false;
      private goToSelect(popupName: string) {
        this.$router.push({ name: 'form', query: { [popupName]: 'true' } });
      }
    
      private onSelectPitDate(...res: DateObject[]) {
        ...
        this.$router.go(-1);
      }
    
      @Watch('$route.query')
      private handlePopup(val: any) {
        switch (true) {
          case val.calendar && val.calendar === 'true':
            this.showCalendar = true;
            break;
          default:
            this.showCalendar = false;
            break;
        }
      }
    }
    </script>
    

     

    通过 UA 获取设备信息

    在开发 h5 开发时,可能会遇到下面几种情况:

    1. 开发时都是在浏览器进行开发调试的,所以需要避免调用 native 的接口,因为这些接口在浏览器环境根本不存在;

    2. 有些情况需要区分所在环境是在 android webview 还是 ios webview,做一些针对特定平台的处理;

    3. 当 h5 版本已经更新,但是客户端版本并没有同步更新,那么如果之间的接口调用发生了改变,就会出现调用出错。

    所以需要一种方式来检测页面当前所处设备的平台类型、app 版本、系统版本等,目前比较靠谱的方式是通过 android / ios webview 修改 UserAgent,在原有的基础上加上特定后缀,然后在网页就可以通过 UA 获取设备相关信息了。当然这种方式的前提是 native 代码是可以为此做出改动的。以安卓为例关键代码如下:

    安卓关键代码:

    // Activity -> onCreate
    ...
    // 获取 app 版本
    PackageManager packageManager = getPackageManager();
    PackageInfo packInfo = null;
    try {
      // getPackageName()是你当前类的包名,0代表是获取版本信息
      packInfo = packageManager.getPackageInfo(getPackageName(),0);
    } catch (PackageManager.NameNotFoundException e) {
      e.printStackTrace();
    }
    String appVersion = packInfo.versionName;
    
    // 获取系统版本
    String systemVersion = android.os.Build.VERSION.RELEASE;
    
    mWebSettings.setUserAgentString(
      mWebSettings.getUserAgentString() + " DSBRIDGE_"  + appVersion + "_" + systemVersion + "_android"
    );
    

    h5 关键代码:

    const initDeviceInfo = () => {
      const UA = navigator.userAgent;
      const info = UA.match(/s{1}DSBRIDGE[w.]+$/g);
      if (info && info.length > 0) {
        const infoArray = info[0].split('_');
        window.$appVersion = infoArray[1];
        window.$systemVersion = infoArray[2];
        window.$platform = infoArray[3] as Platform;
      } else {
        window.$appVersion = undefined;
        window.$systemVersion = undefined;
        window.$platform = 'browser';
      }
    };
    

    mock 数据

    Mock[71]

    当前后端进度不一致,接口还尚未实现时,为了不影响彼此的进度,此时前后端约定好接口数据格式后,前端就可以使用 mock 数据进行独立开发了。本项目使用了 Mock 实现前端所需的接口。

    调试控制台

    eruda[72]

    vconsole[73]

    在调试方面,本项目使用 eruda 作为手机端调试面板,功能相当于打开 PC 控制台,可以很方便地查看 console, network, cookie, localStorage 等关键调试信息。与之类似地工具还有微信的前端研发团队开发的 vconsole,各位可以选择适合自己项目的工具。

    关于 eruda 使用,推荐使用 cdn 方式加载,至于什么时候加载 eruda,可以根据不同项目制定不同策略。示例代码如下:

    <script>
      (function() {
        const NO_ERUDA = window.location.protocol === 'https:';
        if (NO_ERUDA) return;
        const src = 'https://cdn.jsdelivr.net/npm/eruda@1.5.8/eruda.min.js';
        document.write('<scr' + 'ipt src="' + src + '"></scr' + 'ipt>');
        document.write('<scr' + 'ipt>eruda.init();</scr' + 'ipt>');
      })();
    </script>
    

    抓包工具

    charles[74]

    fiddler[75]

    虽然有了 eruda 调试工具,但某些情况下仍不能满足需求,比如现网完全关闭 eruda 等情况。

    此时就需要抓包工具,相关工具主要就是上面罗列的这两个,各位可以选择适合自己项目的工具。

    通过 charles 可以清晰的查看所有请求的信息(注:https 下抓包需要在手机上配置相关证书)。当然 charles 还有更多强大功能,比例模拟弱网情况,资源映射等。

    推荐一篇不错的 charles 使用教程:

    解锁 Charles 的姿势[76]

    异常监控平台

    sentry[77]

    移动端网页相对 PC 端,主要有设备众多,网络条件各异,调试困难等特点。导致如下问题:

    • 设备兼容或网络异常导致只有部分情况下才出现的 bug,测试无法全面覆盖

    • 无法获取出现 bug 的用户的设备,又不能复现反馈的 bug

    • 部分 bug 只出现几次,后面无法复现,不能还原事故现场

    这时就非常需要一个异常监控平台,将异常实时上传到平台,并及时通知相关人员。

    相关工具有 sentry,fundebug 等,其中 sentry 因为功能强大,支持多平台监控(不仅可以监控前端项目),完全开源,可以私有化部署等特点,而被广泛采纳。

    下面是 sentry 在本项目应用时使用的相关配套工具。

    sentry 针对 javascript 的 sdk

    sentry-javascript[78]

    自动上传 sourcemap 的 webpack 插件

    sentry-webpack-plugin[79]

    编译时自动在 try catch 中添加错误上报函数的 babel 插件

    babel-plugin-try-catch-error-report[80]

    补充:

    前端的异常主要有以下几个部分:

    • 静态资源加载异常

    • 接口异常(包括与后端和 native 的接口)

    • js 报错

    • 网页崩溃

    其中静态资源加载失败,可以通过 window.addEventListener('error', ..., true) 在事件捕获阶段获取,然后筛选出资源加载失败的错误并手动上报错误。核心代码如下:

    // 全局监控资源加载错误
    window.addEventListener(
      'error',
      (event) => {
        // 过滤 js error
        const target = event.target || event.srcElement;
        const isElementTarget =
          target instanceof HTMLScriptElement ||
          target instanceof HTMLLinkElement ||
          target instanceof HTMLImageElement;
        if (!isElementTarget) {
          return false;
        }
        // 上报资源地址
        const url =
          (target as HTMLScriptElement | HTMLImageElement).src ||
          (target as HTMLLinkElement).href;
    
        this.log({
          error: new Error(`ResourceLoadError: ${url}`),
          type: 'resource load'
        });
      },
      true
    );
    

    关于服务端接口异常,可以通过在封装的 http 模块中,全局集成上报错误函数(native 接口的错误上报类似,可在项目中查看)。核心代码如下:

    function errorReport(
      url: string,
      error: string | Error,
      requestOptions: AxiosRequestConfig,
      response?: AnyObject
    ) {
      if (window.$sentry) {
        const errorInfo: RequestErrorInfo = {
          error: typeof error === 'string' ? new Error(error) : error,
          type: 'request',
          requestUrl: url,
          requestOptions: JSON.stringify(requestOptions)
        };
    
        if (response) {
          errorInfo.response = JSON.stringify(response);
        }
    
        window.$sentry.log(errorInfo);
      }
    }
    

    关于全局 js 报错,sentry 针对的前端的 sdk 已经通过 window.onerror 和 window.addEventListener('unhandledrejection', ..., false) 进行全局监听并上报。

    需要注意的是其中 window.onerror = (message, source, lineno, colno, error) =>{} 不同于 window.addEventListener('error', ...),window.onerror 捕获的信息更丰富,包括了错误字符串信息、发生错误的 js 文件,错误所在的行数、列数、和 Error 对象(其中还会有调用堆栈信息等)。所以 sentry 会选择 window.onerror 进行 js 全局监控。

    但有一种错误是 window.onerror 监听不到的,那就是 unhandledrejection 错误,这个错误是当 promise reject 后没有 catch 住所引起的。当然 sentry 的 sdk 也已经做了监听。

    针对 vue 项目,也可对 errorHandler 钩子进行全局监听,react 的话可以通过 componentDidCatch 钩子,vue 相关代码如下:

    // 全局监控 Vue errorHandler
    Vue.config.errorHandler = (error, vm, info) => {
      window.$sentry.log({
        error,
        type: 'vue errorHandler',
        vm,
        info
      });
    };
    

    但是对于我们业务中,经常会对一些以报错代码使用 try catch,这些错误如果没有在 catch 中向上抛出,是无法通过 window.onerror 捕获的,针对这种情况,笔者开发了一个 babel 插件 babel-plugin-try-catch-error-report[81],该插件可以在 babel[82] 编译 js 的过程中,通过在 ast 中查找 catch 节点,然后再 catch 代码块中自动插入错误上报函数,可以自定义函数名,和上报的内容(源码所在文件,行数,列数,调用栈,以及当前 window 属性,比如当前路由信息 window.location.href)。相关配置代码如下:

    if (!IS_DEV) {
      plugins.push([
        'try-catch-error-report',
        {
          expression: 'window.$sentry.log',
          needFilename: true,
          needLineNo: true,
          needColumnNo: false,
          needContext: true,
          exclude: ['node_modules']
        }
      ]);
    }
    

    针对跨域 js 问题,当加载的不同域的 js 文件时,例如通过 cdn 加载打包后的 js。如果 js 报错,window.onerror 只能捕获到 script error,没有任何有效信息能帮助我们定位问题。此时就需要我们做一些事情:第一步、服务端需要在返回 js 的返回头设置 Access-Control-Allow-Origin: *第二部、设置 script 标签属性 crossorigin,代码如下:

    <script src="http://helloworld/main.js" crossorigin></script>
    

    如果是动态添加的,也可动态设置:

    const script = document.createElement('script');
    script.crossOrigin = 'anonymous';
    script.src = url;
    document.body.appendChild(script);
    

    针对网页崩溃问题,推荐一个基于 service work 的监控方案,相关文章已列在下面的。如果是 webview 加载网页,也可以通过 webview 加载失败的钩子监控网页崩溃等。

    如何监控网页崩溃?[83]

    最后,因为部署到线上的代码一般都是经过压缩混淆的,如果没有上传 sourcemap 的话,是无法定位到具体源码的,可以现在 项目中添加 .sentryclirc 文件,其中内容可参考本项目的 .sentryclirc,然后通过 sentry-cli (需要全局全装 sentry-cli 即npm install sentry-cli)命令行工具进行上传,命令如下:

    sentry-cli releases -o 机构名 -p 项目名 files 版本 upload-sourcemaps sourcemap 文件相对位置 --url-prefix js 在线上相对根目录的位置 --rewrite
    // 示例
    sentry-cli releases -o mcukingdom -p hello-world files 0.2.1 upload-sourcemaps dist/js --url-prefix '~/js/' --rewrite
    

    当然官方也提供了 webpack 插件 sentry-webpack-plugin[84],当打包时触发 webpack 的 after-emit 事件钩子(即生成资源到 output 目录之后),插件会自动上传打包目录中的 sourcemap 和关联的 js,相关配置可参考本项目的 vue.config.js 文件。

    通常为了安全,是不允许在线上部署 sourcemap 文件的,所以上传 sourcemap 到 sentry 后,可手动删除线上 sourcemap 文件。

    常见问题

    • iOS WKWebView cookie 写入慢以及易丢失

      现象:

      原因:WKWebView 对 NSHTTPCookieStorage 写入 cookie,不是实时存储的。从实际的测试中发现,不同的 IOS 版本,延迟的时间还不一样。同样,发起请求时,也不是实时读取,无法做到和 native 同步,导致页面逻辑出错。

      两种解决办法:

      各位可以选择适合自己项目的方式,有更好的处理方式欢迎留言。

      1. 客户端手动干预一下 cookie 的存储。将服务响应的 cookie,持久化到本地,在下次 webview 启动时,读取本地的 cookie 值,手动再去通过 native 往 webview 写入。但是偶尔还有 spa 的页面路由切换的时候丢失 cookie 的问题。

      2. 将 cookie 存储的 session 持久化到 localSorage,每次请求时都会取 localSorage 存储的 session,并在请求头部添加 cookieback 字段,服务端鉴权时,优先校验 cookieback 字段。这样即使 cookie 丢失或存储的上一次的 session,都不会有影响。不过这种方式相当于绕开了 cookie 传输机制,无法享受 这种机制带来的安全特性。

      1. iOS 登陆后立即进入网页,会出现 cookie 获取不到或获取的上一次登陆缓存的 cookie

      2. 重启 App 后,cookie 会丢失

    • input 标签在部分安卓 webview 上无法实现上传图片功能

      因为 Android 的版本碎片问题,很多版本的 WebView 都对唤起函数有不同的支持。我们需要重写 WebChromeClient 下的 openFileChooser()(5.0 及以上系统回调 onShowFileChooser())。我们通过 Intent 在 openFileChooser()中唤起系统相机和支持 Intent 的相关 app。

      相关文章:【Android】WebView 的 input 上传照片的兼容问题[85]

    • input 标签在 iOS 上唤起软键盘,键盘收回后页面不回落(部分情况页面看上去已经回落,实际结构并未回落)

      input 焦点失焦后,ios 软键盘收起,但没有触发 window resize,导致实际页面 dom 仍然被键盘顶上去--错位。解决办法:全局监听 input 失焦事件,当触发事件后,将 body 的 scrollTop 设置为 0。

      document.addEventListener('focusout', () => {
        document.body.scrollTop = 0;
      });
      
    • 唤起软键盘后会遮挡输入框

      当 input 或 textarea 获取焦点后,软键盘会遮挡输入框。解决办法:全局监听 window 的 resize 事件,当触发事件后,获取当前 active 的元素并检验是否为 input 或 textarea 元素,如果是则调用元素的 scrollIntoViewIfNeeded 即可。

      window.addEventListener('resize', () => {
        // 判断当前 active 的元素是否为 input 或 textarea
        if (
          document.activeElement!.tagName === 'INPUT' ||
          document.activeElement!.tagName === 'TEXTAREA'
        ) {
          setTimeout(() => {
            // 原生方法,滚动至需要显示的位置
            document.activeElement!.scrollIntoView();
          }, 0);
        }
      });
      
    • 唤起键盘后 position: fixed;bottom: 0px; 元素被键盘顶起

      解决办法:全局监听 window 的 resize 事件,当触发事件后,获取 id 名为 fixed-bottom 的元素(可提前约定好如何区分定位在窗口底部的元素),将其设置成 display: none。键盘收回时,则设置成 display: block;

      const clientHeight = document.documentElement.clientHeight;
      window.addEventListener('resize', () => {
        const bodyHeight = document.documentElement.clientHeight;
        const ele = document.getElementById('fixed-bottom');
        if (!ele) return;
        if (clientHeight > bodyHeight) {
          (ele as HTMLElement).style.display = 'none';
        } else {
          (ele as HTMLElement).style.display = 'block';
        }
      });
      
    • 点击网页输入框会导致网页放大通过 viewport 设置 user-scalable=no 即可,(注意:当 user-scalable=no 时,无需设置 minimum-scale=1, maximum-scale=1,因为已经禁止了用户缩放页面了,允许的缩放范围也就不存在了)。代码如下:

      <meta
        name="viewport"
        content="width=device-width,initial-scale=1.0,user-scalable=0,viewport-fit=cover"
      />
      
    • webview 通过 loadUrl 加载的页面运行时却通过第三方浏览器打开,代码如下

      // 创建一个 Webview
      Webview webview = (Webview) findViewById(R.id.webView);
      // 调用 Webview loadUrl
      webview.loadUrl("http://www.baidu.com/");
      

      解决办法:在调用 loadUrl 之前,设置下 WebviewClient 类,当然如果需要也可自己实现 WebviewClient(例如通过拦截 prompt 实现 js 与 native 的通信)

      webview.setWebViewClient(new WebViewClient());
    展开全文
  • 一、搭建vue-cli脚手架 这个过程就不详细说明了不清楚的可以翻阅我以前的有关vue脚手架的文章 二、安装Typescript相关依赖 npm i vue-class-component vue-property-decorator --save npm i ts-loader ...

    一、搭建vue-cli脚手架
    这个过程就不详细说明了不清楚的可以翻阅我以前的有关vue脚手架的文章
    二、安装Typescript相关依赖
    npm i vue-class-component vue-property-decorator --save
    npm i ts-loader typescript tslint tslint-loader tslint-config-standard --save-dev

    • vue-class-component:强化 Vue 组件,使用 TypeScript/装饰器 增强 Vue 组件

    • vue-property-decorator:在 vue-class-component 上增强更多的结合 Vue 特性的装饰器如下:
      @Emit
      @Inject
      @Model
      @Prop
      @Provide
      @Watch
      @Component (从 vue-class-component 继承)

    • ts-loader:TypeScript 为 Webpack 提供了 ts-loader,其实就是为了让webpack识别 .ts .tsx文件

    • tslint-loadertslint:我想你也会在.ts .tsx文件 约束代码格式(作用等同于eslint)

    • tslint-config-standard:tslint 配置 standard风格的约束

    三、配置webpack
    (1)找到文件build/webpack.base.conf.js
    找到entry.app 将main.js 改成 main.ts, 顺便把项目文件中的main.js也改成main.ts, 里面内容保持不变
    (2)找到resolve.extensions 里面加上.ts 后缀 (是为了之后引入.ts的时候不写后缀)

    resolve: {
    	    extensions: ['.js', '.vue', '.json', '.ts'],
    	    alias: {
    	      '@': resolve('src')
    	    }
     }
    

    (3)找到module.rules 添加webpack对.ts的解析

    module: {
      rules: [
        {
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          enforce: 'pre',
          include: [resolve('src'), resolve('test')],
          options: {
            formatter: require('eslint-friendly-formatter')
          }
        },
    // 开始
        {
          test: /\.ts$/,
          exclude: /node_modules/,
          enforce: 'pre',
          loader: 'tslint-loader'
        },
        {
          test: /\.tsx?$/,
          loader: 'ts-loader',
          exclude: /node_modules/,
          options: {
            appendTsSuffixTo: [/\.vue$/],
          }
        },
    // 复制从开始以上的即可
      }
    }
    

    四、添加配置 tsconfig.json 和 tslint.json文件
    (1)在根路径下创建tsconfig.json文件,以下是简单的配置,详细的可访问http://json.schemastore.org/tsconfig

    {
      "include": [
        "src/**/*"
      ],
      "exclude": [
        "node_modules"
      ],
      "compilerOptions": {
        "allowSyntheticDefaultImports": true,
        "experimentalDecorators": true,
        "allowJs": true,
        "module": "esnext",
        "target": "es5",
        "moduleResolution": "node",
        "isolatedModules": true,
        "lib": [
          "dom",
          "es5",
          "es2015.promise",
          "scripthost"
        ],
        "sourceMap": true,
        "pretty": true,
        "strictFunctionTypes": false,
        "importHelpers": true
      }
    }
    

    (2)在根路径下创建tslint.json文件,就是 引入 ts 的 standard 规范

    {
      "extends": "tslint-config-standard",
      "globals": {
        "require": true
      }
    }
    

    五、识别 .vue
    由于 TypeScript 默认并不支持 *.vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个 vue-shim.d.ts 文件,放在项目项目对应使用目录下,例如 src/vue-shim.d.ts

    declare module "*.vue" {
      import Vue from "vue";
      export default Vue;
    }
    

    注意:在引入组件的使用加.vue 因为TypeScript 默认只识别 *.ts 文件,不识别 *.vue 文件:,例如import Component from 'components/component.vue'
    六、改造.vue 文件
    <template></template>和<style></style>内的内容和vue一样,主要修改<script lang="ts"></script>内的代码格式, 注意:在script 标签上加上 lang=“ts”, 意思是让webpack将这段代码识别为typescript 而非javascript,简单的代码格式如下,注意: @Component必须有没有子组件可以为空
    (1)parent.vue

    <script lang="ts">
        import Vue from 'vue';
        import Component from 'vue-class-component';
        import Child from '../../components/Child.vue';
        @Component({
            components: {
                Child
            },  
        })
        export default class parent extends Vue {
            num = 0;
            created(){
               
            }
        }
    </script>
    

    (2)child.vue(子组件获取父组件的数据@Prop)

      import Vue from 'vue';
      import Component from 'vue-class-component';
      import { Prop } from 'vue-property-decorator'
    
       @Component
       export class Child extends Vue {
    	     @Prop()
    	     num: number
    	    created(){
                console.log(this.num);
            }
       }
    

    六、使用less
    (1)安装 npm install less -s
    (2) 找到文件build/webpack.base.conf.js里的module.rules 添加webpack对.ts的解析

      {
            test: /\.less$/,
            loader: "style-loader!css-loader!less-loader"
       },
    

    七、使用Axios
    在常规的安装了axios发现报错,后来百度换了一种思路就可以了
    因为在ts中,不识别vue下面挂$axios,不可以挂在原型链上
    (1)安装axios和vue-axios npm install axios vue-axios
    (2)在main.ts中引入全局使用

    	import axios from 'axios'
    	import Vueaxios from 'vue-axios'
    	Vue.use(Vueaxios, axios)
    

    (3)在组件中直接this.axios.get/post直接使用

    八、使用Vuex
    今天先到这里改天继续完善,欢迎指正交流

    展开全文
  • 技术淘宝

    千次阅读 2019-06-29 10:22:53

    ? ? ? ?  精度前端学习 —— 前端开发100天(置顶)

    http://alloyteam.github.io/CodeGuide/
    https://github.com/AlloyTeam/CodeGuide

     

    cmd控制台的小技巧:可以直接将文件夹/文件丢进去,这样就会打印出该路径了。

     

    舒服的字体家族样式:

    font: 300 16px/1.8 -apple-system,PingFang SC,Verdana,Helvetica Neue,Microsoft Yahei,Hiragino Sans GB,Microsoft Sans Serif,WenQuanYi Micro Hei,sans-serif;
    font-size: 62.5%;
    background: #fafafa;
    text-rendering: optimizelegibility;
    -webkit-text-size-adjust: 100%;
    -webkit-font-smoothing: antialiased;

     

    GitClub :不仅仅是一个 GitHub 客户端,还是一个发现优秀 GitHub 开源项目的 App

    https://github.com/TellH/GitClub/raw/dev/apk/app-release.apk

     

    video grabber:是一个实用的视频下载工具。它支持下载优酷、爱奇艺、哔哩哔哩、搜狐等其他网站的视频,只需要复制你要下载的视频连接到网站,就可以直接下载视频。它还支持在线录制屏幕和视频格式转换等等功能.

    https://www.videograbber.net/

     

    getemoji.com:为文章添加活力

    http://getemoji.com/
    https://emojikeyboard.org/

     

    分享、推荐 GitHub 上好玩、容易上手的项目,帮你找到编程的乐趣。 

    https://hellogithub.com/volume/15/

     

    极小的 JavaScript 画板: 超级平滑优雅

    http://fiala.uk/atrament.js/demo/

     

    敏感词字典:

    https://github.com/observerss/textfilter

     

    C# Newtonsoft.Json  是一款 .NET 平台中开源的 JSON 序列化和反序列化类库

    https://github.com/JamesNK/Newtonsoft.Json

     

     

    php: 基于词库的中文转拼音优质解决方案

    https://github.com/overtrue/pinyin

     

    nodePPT:这可能是迄今为止最好的网页版演示库

    http://js8.in/nodeppt/

     

     

    动画库:tweenjs/CreateJS

    http://www.createjs.cc/tweenjs/

     

    新的手势库touch: zingtouch

    https://github.com/zingchart/zingtouch

     

    share.js : 一键分享到微博、QQ空间、QQ好友、微信、腾讯微博、豆瓣、Facebook、Twitter、Linkedin、Google+、点点等

    https://github.com/overtrue/share.js 

     

     

    手掌指针

        cursor: url(https://webapi.amap.com/theme/v1.3/openhand.cur),default;

     

     

    jQuery动效动画插件库

    http://www.jq22.com/

     

     

    滚动视差示例

    https://mp.weixin.qq.com/s/XC1kcY-_BjaCrwjszeOXDg
    http://sc.chinaz.com/tag_jiaoben/ShiChaGunDong.html http://www.wenliku.com/sheji/6290.html

     

     

    国产山寨的postman: apizza

    http://apizza.cc

     

    HTML5/CSS3/WebGL/SVG动效库

    http://techbrood.com
    http://www.jsdaima.com/js
    http://www.17sucai.com/search/
    http://down.admin5.com/texiao/yanse/ http://down.admin5.com/demo/code_pop/21/11/ http://www.jsdaima.com/js/demo/2210.html http://www.jsdaima.com/js/demo/2155.html

     

     

    一个不错的动效网站

    https://reeoo.com/tag/company

     

     

     

    CodeSandbox: 一个超屌的在线代码编辑演示网站
    https://codesandbox.io/

     

    不错的录屏工具 : EV录屏

    https://www.ieway.cn/evcapture.html

     

    不错的滚动条插件

    http://utatti.github.io/perfect-scrollbar/

     

    run.bat: 用ie打开指定网站

    set link=http://120.196.128.45:801/
    Start iexplore "%link%"

     

    git客户端管理工具

    github_desktop: https://desktop.github.com/
    
    sublime_merge : https://forum.sublimetext.com/c/sublime-merge
    
    gitkraken: https://www.gitkraken.com/download/windows64

     

    SSH工具推荐:MobaXterm

    https://mobaxterm.mobatek.net/

     

     Chrome 定住 hover元素的奇思淫巧

    简而言之,就是通过ctrl + shift + c 呼控制台快速选中,然后右键呼出chrome菜单。鼠标移到控制台处,这时候就可以为所欲为了。

     

    配色好网站

    https://webkul.github.io/coolhue/
    https://webgradients.com/

     

     

     

    TJ大神,不读书不看视频,仅靠看代码成神

    https://zhuanlan.zhihu.com/p/19572823

     

     

    nodejs 轻量级服务器:http-server

    $ npm install http-server -g
    $ http-server

     

     

    Nodejs的ORM: sequelize

    https://github.com/sequelize/sequelize
    
    https://nodelover.me/course/sequelize/1

     

     

    FSCapture:经典优秀屏幕截图录像工具,内含取色器

    官网:https://www.filecluster.com/downloads/FastStone-Capture.html

    链接:https://pan.baidu.com/s/1Ozo-sv47nlJHPzPuzrd6ZA 提取码:qx7m

     

    蓝灯官方下载地址

    https://github.com/getlantern/download#

    # 如果只是想上google,可以使用 chromego-lite,请百度自行搜索

     

     

    一个仿 trello 的工具:leangoo

    https://www.leangoo.com/kanban/board/go/2515534#

     

     

    移动端骨架图解决方案,请注意不支持hash模式,请切换到history

    https://github.com/ElemeFE/page-skeleton-webpack-plugin/blob/master/docs/i18n/zh_cn.md

     

    MP4 转 gif 在线服务

    https://convertio.co/zh/mp4-gif/

     

    gif 在线压缩

    http://www.soogif.com/compress

     

     

    Postcss 与 Autoprefixer 的 browserslist 浏览器配置

    https://github.com/browserslist/browserslist#queries
    http://browserl.ist/?q=defaults

     

    一些前端github学习资料

    // 牛逼的30秒库
    https://github.com/Chalarangelo/30-seconds-of-code/blob/master/snippets/curry.md
    
    // 前端面试手册
    https://github.com/yangshun/front-end-interview-handbook/blob/master/Translations/Chinese/README.md
    
    // 算法与数据结构
    https://github.com/trekhleb/javascript-algorithms
    
    // (新)前端面试题
    https://github.com/qiu-deqing/FE-interview
    
    // 腾讯移动Web前端知识库
    https://github.com/AlloyTeam/Mars
    
    // Build your own 系列,各种环境的构建技术集合,包含了很多自己动手的实践方法
    https://github.com/danistefanovic/build-your-own-x

     

     

     

    nginx 配置支持URL HTML5 History 模式

    location / {
       try_files $uri /index.html;
    }

     

     

    很不错的纯JavaScript富文本

    https://github.com/ckeditor/ckeditor5

     

    vs ide下载地址

    https://visualstudio.microsoft.com/zh-hans/vs/

     

    印象笔记网页裁剪插件

    https://www.yinxiang.com/webclipper/install/

     

    Oracle 11g数据库详细安装步骤图解

    https://blog.csdn.net/projectNo/article/details/76533921
    http://download.oracle.com/otn/nt/oracle11g/112010/win64_11gR2_database_2of2.zip

     

    python 学习算法的好地方

    https://facert.gitbooks.io/python-data-structure-cn/2.算法分析/2.2.什么是算法分析/

     

    页面代码高亮插件:highlightjs 多种风格

    https://github.com/highlightjs/highlight.js 

     

    如何删除onenode上的文档,必须在网上才可以

    https://onedrive.live.com/about/zh-cn/

     

      

     

    意外很火的Select下拉菜单美化插件:chosen

    https://harvesthq.github.io/chosen/
    https://github.com/harvesthq/chosen

     

     

    一个朋友给的基于tp的代码生成器

    svn://123.207.127.201/codeclide/

     

    navicat 绿色版下载地址

    http://download.jtthink.com/144.rar?e=1532661002&token=8s8XS2gzzrN6s0pclUF2TQxK6Zfa3bQ0iUNo1VjW:m_Uy4G91fSLZXWYvNESA7QVxX2M=

     

    在线编写API的地方

    https://apizza.net/

     

    一个不错的php根据注解自动生成文档的脚本

    https://github.com/opqnext/reflection_api_doc

     

    Nodejs 版selenium ,总是记不得名字,现在好好记一下吧 puppeteer(傀儡师)

    教程:https://github.com/dragon8github/puppeteer-examples

    https://github.com/GoogleChrome/puppeteer?utm_source=gold_browser_extension

     

    画思维导图和类图的好工具:xmind

    https://www.xmind.net/download/win/

     

    chrome扩展插件的本地地址

    如何找到的?首先进入chrome插件管理中心,找到插件的id,在电脑中搜索一下即可。

    # win10
    C:\Users\Lee\AppData\Local\Google\Chrome\User Data\Default\Extensions\

     

    React Tool 开发工具

    https://github.com/facebook/react-devtools
    https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi/related

     

    在线写前端代码的好地方:https://stackblitz.com/

    https://stackblitz.com/
    https://stackblitz.com/edit/dva-example-count?file=index.js

     

    github可视化浏览/树形浏览插:OctoTree

    https://github.com/buunguyen/octotree

     

     

    看看人家的组件化和可视化是如何诞生的

    https://motion.ant.design/edit/#t%3Dnav_0_0%2Ccontent_0_0%2Ccontent_2_0%2Ccontent_3_0%2Ccontent_4_0%2Cfooter_0_0

     

     

    新的异常监控平台 http://rollbar.com ,蚂蚁金服ant-design都在使用。

    https://rollbar.com

     

    react小书

    http://huziketang.mangojuice.top/books/react/lesson1

     

    sublime加入到右键菜单中

    https://jingyan.baidu.com/article/cdddd41c99d07653ca00e147.html

    新建 sublime_addright.reg,放入以下代码,然后运行即可

    Windows Registry Editor Version 5.00
    
    [HKEY_CLASSES_ROOT\*\shell\SublimeText3]
    
    @="用 SublimeText3 打开"
    
    "Icon"="C:\\Program Files\\Sublime Text 3\\sublime_text.exe,0"
    
    [HKEY_CLASSES_ROOT\*\shell\SublimeText3\command]
    
    @="C:\\Program Files\\Sublime Text 3\\sublime_text.exe %1"
    
    [HKEY_CLASSES_ROOT\Directory\shell\SublimeText3]
    
    @="用 SublimeText3 打开"
    
    "Icon"="C:\\Program Files\\Sublime Text 3\\sublime_text.exe,0"
    
    [HKEY_CLASSES_ROOT\Directory\shell\SublimeText3\command]
    
    @="C:\\Program Files\\Sublime Text 3\\sublime_text.exe %1"

     

     

    从第三方框架 grafana 中学习webpack4.0的配置,以及ts的配置

    https://github.com/grafana/grafana/tree/master/scripts/webpack

     

    免费书籍大全

    https://www.cnblogs.com/think90/p/8159178.html

     

     

    sureface 屏幕残影问题官方解决方案 - 卸载显卡驱动

    您进入桌面,左下角微软图标(单击右键),选择设备管理器,点开“显示适配器”前面的小三角,找到“Intel(r) hd gRAPHICS 520”, 单击右键卸载,卸载的时候不要勾选“删除此设备的驱动软件”,卸载完之后,重启设备。

     

    屏幕按键、按钮录制、屏幕字幕

    https://github.com/Code52/carnac/tree/2.2.155

     

     

     

    4分钟!教你制作一段音乐

    https://www.bilibili.com/video/av1390915/?spm_id_from=333.334.bili_technology.4

      

    极验验证码

    http://jiyandoc.c2567.com/

    https://github.com/dragon8github/forevery_happy/blob/master/App/WinFormApp/Fuck.cs#L241

      

    为【新建文档】添加快捷键

    https://www.cnblogs.com/pinsily/p/7635094.html

    通常新建文件夹都是,右键 + WF。但常用的新建文档居然没有快捷键,其实是可以设置的。

    1、打开注册表(win+R ,输入 regedit)

    2、定位到 HKEY_CLASSES_ROOT\Local Settings\MuiCache\63\AAF68885

    3、之后找到右侧的 @C:\Windows\system32\notepad.exe,-469,将 文本文档 改为 文本文档(&T)

     

    unix 的 rz命令,上传本地文件。了解一下

    https://www.jb51.net/article/73690.htm

     

    桌面端vuejs开发 vuido

    https://github.com/mimecorg/vuido

     

    前端面试题锦集

    https://github.com/yangshun/front-end-interview-handbook/blob/master/Translations/Chinese/README.md

      

    github emoji list

    http://www.emoji-cheat-sheet.com/

     

    格式美化、优化、html/js/css 美化

    http://jsbeautifier.org/
    https://github.com/beautify-web/js-beautify

     

    ps photoshop cc 2015 下载

    # PS CC2015 下载地址
    http://pan.baidu.com/s/1gfJUEzT
    
    # 切图教程
    https://blog.csdn.net/xiaoermingn/article/details/53243331

     

     

    Sourcegraph 更好的预览github

    https://about.sourcegraph.com/
    https://chrome.google.com/webstore/detail/dgjhfomjieaadpoljlnidmbgkdffpack

      

     

    Redis Desktop Manager

    官网地址下载地址有点问题,fq都不行,只能找民间的了

    https://redisdesktop.com/download

      http://down-www.7down.net/pcdown/soft/R/redis-desktop-manager.rar

     

     

    设置默认的输入法切换,不要浪费时间切换美式中文

     

     

    window开机启动

    window + R, 输入: shell:startup

     

    淘宝前端ppt

    http://2014.jsconf.cn/slides/herman-taobaoweb/index.html#/

      

    生活大爆炸微云盘

    https://share.weiyun.com/b71a38d2308ca84846359de0f3c2010a

     

    狼叔的全栈之路

    https://github.com/i5ting/How-to-learn-node-correctly?utm_source=gold_browser_extension#%E6%88%91%E7%9A%84%E5%85%A8%E6%A0%88%E4%B9%8B%E8%B7%AF

     

    强大无比的离线开发文档

    笔者推荐使用 Dash 或 Zeal 查看离线文档

    mac ox :https://kapeli.com/dash

    window/mac/unix:https://zealdocs.org/

     

    宏命令软件

    视频介绍(6分钟开始是重点演示):
    https://www.youtube.com/watch?v=Arn8ExQ2Gjg&feature=youtu.be&t=362

    bilibili
    https://www.bilibili.com/video/av4407367

    示例代码
    https://github.com/TaranVH/2nd-keyboard

    luamacros 官网:
    http://www.hidmacros.eu/forum/viewtopic.php?f=10&t=241#p794

    github:
    https://github.com/me2d13/luamacros

    原理是通过第二个键盘,写入txt,然后再发送快捷键,让ahk读取txt,然后再一次触发。
    https://github.com/TaranVH/2nd-keyboard/blob/master/LUAMACROS/2nd%20keyboard%20if%20using%20luamacros.ahk#L37:1
    https://github.com/TaranVH/2nd-keyboard/blob/master/LUAMACROS/SECOND%20KEYBOARD%20script%20for%20LUA%20MACROS.lua

     

    打造个人品牌

    https://juejin.im/entry/5ad4cc575188255566702c36?utm_source=gold_browser_extension

     

    jenkins打造前端自动化部署

    https://juejin.im/post/5ad1980e6fb9a028c42ea1be?utm_source=gold_browser_extension

     

     

    hosts所在文件夹:

    Windows 系统hosts位于 C:\Windows\System32\drivers\etc\hosts
    Android(安卓)系统hosts位于 /etc/hosts
    Mac(苹果电脑)系统hosts位于 /etc/hosts
    iPhone(iOS)系统hosts位于 /etc/hosts
    Linux系统hosts位于 /etc/hosts
    绝大多数Unix系统都是在 /etc/hosts

     

     

    完全用Linux工作

    https://www.cnblogs.com/skyseraph/archive/2010/10/30/1865280.html

     

     

    下载google play的东西

    https://apkpure.com/cn/

     

     

    超级强大的表单插件

    https://handsontable.com/

     

    让小程序支持组件化开发的框架一个最受欢迎的小程序框架.

    https://tencent.github.io/wepy/
    https://github.com/Tencent/wepy

     

    不好意思!耽误你的十分钟,让MVVM原理还给你

    https://juejin.im/post/5abdd6f6f265da23793c4458?utm_source=gold_browser_extension

     

    从只会git add .的菜鸟到掌握git基本功能

    https://juejin.im/post/5abef8356fb9a028df22bd78?utm_source=gold_browser_extension

     

    身份证验证

    https://juejin.im/post/5aa8d89af265da23866f9669

     

    阿里云最便宜的乞丐配置云服务器购买

    # 首页顶部 -  最新活动 - 特惠专区 - 云服务器特惠
    https://promotion.aliyun.com/ntms/act/qwbk.html?spm=5176.8112568.738194.2.227d9ed5npRzJ2

     

    大厂 UI 开发实战手册

    https://juejin.im/book/5a7bfe595188257a7349b52a/?ref=o2

     

    一个值得前端开发者学习的css框架:bulma

    https://bulma.io/
    https://github.com/jgthms/bulma/
    http://www.ruanyifeng.com/blog/2017/10/bulma.html

     

    监听用户的后退键,解决部分浏览器回退的bug

    $(document).keydown(function (event) {
        // 监听backspace键    
        if (event.keyCode == 8 && ['body', 'document', 'window'].indexOf(event.target.tagName.toLocaleLowerCase()) >= 0) {
           return false;
        }
    });

     

    66宏按键键盘,价值1850

    https://www.amazon.cn/dp/B00O1ASXKO/ref=sr_1_fkmr0_1?ie=UTF8&qid=1520814119&sr=8-1-fkmr0&keywords=genovation-controlpad-cp48

    # 如果只是想体验宏键盘,推荐这个
    https://www.ebay.com/p/Igrmyn7464-Genovation-ControlPad-Cp48-USB-HID/2256039012?iid=332262845351

     

    react源码解析

    https://juejin.im/post/5a84682ef265da4e83266cc4?utm_source=gold_browser_extension

     

     

    微信小程序案例大全

    https://www.cnblogs.com/icyhoo/p/6282574.html

     

    字体家族推荐

    font-family: "Segoe UI", "Lucida Grande", Helvetica, Arial, "Microsoft YaHei", FreeSans, Arimo, "Droid Sans","wenquanyi micro hei","Hiragino Sans GB", "Hiragino Sans GB W3", Arial, sans-serif;
      font-family: -apple-system-font,Helvetica Neue,Helvetica,"Microsoft YaHei",sans-serif;

     

     

    Mac 上的免费mysql管理工具:sequelpro

    http://www.sequelpro.com/

     

     

    有空读一读:《vuejs源码解析》《用 npm script 打造超溜的前端工作流》

    https://github.com/answershuto/learnVue
    https://juejin.im/book/5a1212bc51882531ea64df07
    https://juejin.im/book/5a36661851882538e2259c0f

     

     

    脚本之家变成了电子书平台了

    http://www.jb51.net/do/book.html?hshgtrgtr

      

    火币网

    https://www.hadax.com/zh-cn/vote/

      

    fq蓝灯Lantern最新下载地址

    https://github.com/getlantern/forum

     

    符号收集

    ◄ ✕ ■ ►
    ⏰ ? ? ? ? ?
    ? ? ? ? ? ?
    ? ? ? ? ? ?
    ✏ ️? ? ? ? ? ?
    ? ? ? ? ? ? ? ?
    ?? ? ? ?  ? ? ? ☦️ ? ?

     

    使用hexo搭建个人博客

    https://hexo.io/

      

    使用hugo快速生成静态网页,搭建个人博客,还支持大量的皮肤哦,社区也不错

    https://gohugo.io/
    http://www.gohugo.org/
    https://gohugo.io/getting-started/
    http://www.gohugo.org/post/coderzh-hugo/
    https://github.com/gohugoio/hugoThemes


    好好学习的科学方法、***

    https://github.com/Alvin9999/new-pac/wiki

    mac用户:
    https://github.com/Alvin9999/new-pac/wiki/苹果电脑MACFQ软件
    滑动最底部下载蓝灯

     

    用AOS代替WOW滚动动画

    http://michalsnik.github.io/aos/

     

    基于nodejs、mongodb的cms管理框架

    http://keystonejs.com/zh/docs/getting-started/

      

    有意思的小型项目,让信用卡、银行卡表单输入变得更加有趣生动。

    https://github.com/jessepollak/card

      

    跨浏览器的桌面通知插件 push.js

    https://pushjs.org/#

      

    TIM实时共享表格、word

    https://docs.qq.com/desktop/

     

    让分享代码变得高逼格一点

    https://carbon.now.sh/

      

    GIF录制工具GifCam

    https://gifcam.en.softonic.com

     

    屏幕录制软件推荐BB FlashBack Pro 5 Recorder

    BB FlashBack Pro 5 Recorder

      

    自动截图工具Snipaste

    https://zh.snipaste.com/download.html

      

    阿里云备案流程

    https://beian.aliyun.com/
    https://help.aliyun.com/knowledge_detail/36938.html

    前面资料只要仔细认真填写就没问题了。

    值得一提的是第三步【上传备案资料 - 提交身份证和核验单】
    核验单需要下载打印,最底部填上承诺和姓名,日期,然后拍张上传。

    如果步骤错误会有电话给你,成功也会打电话给你,让你【关闭隐私保护
    】:https://jingyan.baidu.com/article/c74d6000bbe9110f6a595d16.html
    隐私保护在域名管理平台-【安全】处可以设置。

    下一步就是提供【万网背景布真人照片
    】,背景布需要申请,送到之后拍照上传即可。比较麻烦

     

    surface 重装系统的解决方案

    1.直接云恢复,云下载恢复镜像,需要wifi,由于有5.6G大小,所以需要长时间,需要电源支持。具体操作方式是重启/开机后,surface检测到异常自动进入修复界面,然后疑难故障->从云恢复即可
    2.手动下载官方恢复镜像 + U盘
    https://support.microsoft.com/zh-cn/surfacerecoveryimage
    请注意,下载链接会被屏蔽掉,所以需要手动关闭屏蔽

      

    surface 启动blos的方式,U盘启动的方式

    关闭Surface →按住音量上调键 →打开Surface,不要松开音量上调键 

     

    华为手机助手HiSuite

    http://consumer.huawei.com/minisite/HiSuite_cn/

      

    Pendo App:神级流程TODO App

    https://www.jianshu.com/p/a40dc034a612

     

    炫酷的canvas粒子效果

    http://wow.techbrood.com/fiddle/26587
    http://www.html5tricks.com/demo/html5-canvas-particle-effect/index.html
    https://www.iviewui.com/

     

    轻量安全的SSH工具 putty:用来代替xshell

    https://www.baidu.com/s?ie=UTF-8&wd=putty

     

    sass的最新框架——bourbon

    有点像Javascript和jQuery、Ruby和Rails、python和Django的关系。

    https://www.bourbon.io/docs/latest/#all-buttons
    https://github.com/thoughtbot/bourbon#installation

     

    晓梦大师的gulp脚手架

    https://github.com/rat1991/actflow

     

    颜文字

    https://www.douban.com/note/201805227/

     

    《webpack从入门到精通》(最好抄过来先,免得什么时候突然下线了)

    setInterval(() => { document.querySelector('.gitbook-plugin-modal').style.zIndex='-1' }, 500);

     Adblock Plus 插件也行
    http://webpack.wuhaolin.cn/

     

    jsbin: 在线写HTML + CSS + JS代码的好地方

    http://jsbin.com

     

    觉得不错的博客

    https://www.zhaokeli.com/Article/6352.html

     

    手机真机调试远程调试

    http://www.v2ex.com/t/295004#reply24

     

    Esay API:好用的API服务平台

    https://www.easyapi.com/

     

    javascript 异常处理的最佳实践(如果改为window.onerror 捕获那岂不是美滋滋)。

    其实是开玩笑来着。

    try {
        // dosomething
    } catch(e) {
        window.open("https://stackoverflow.com/search?q=[js]+" + e.message);
        window.open('https://www.google.com/search?q=javascript+' + e.message);
        window.open('https://segmentfault.com/search?q=javascript+' + e.message);
        window.open('https://www.baidu.com/s?ie=UTF-8&wd=javascript+' + e.message);
    }

     

    adminlte朋友推荐的后台模板:

    https://adminlte.io/

     

    前端路线总结

    http://bbs.jointforce.com/topic/19501

     

    人人影视客户端:追美剧的神器

    http://zmz001.com/1fy8T4

     

    微云:百度云的替代品

    https://share.weiyun.com/b71a38d2308ca84846359de0f3c2010a

     

    查看github上排名前10的作者

    followers:>=1000
    https://help.github.com/articles/about-searching-on-github/

     

    盘搜

    http://www.pansou.com/

     

    js获取元素和window(相对、绝对)高度

    http://www.ruanyifeng.com/blog/2009/09/find_element_s_position_using_javascript.html

     

    dedecms后台美化(外包用)

    https://www.dedemao.com/dedemuban/other/2096.html

     

    raven / sentry: javascript异常捕捉平台

    注意点:1是测试了chrome浏览器可以正常加载cdn地址,而某些浏览器加载不了。2是延迟发送email报告,大概延迟1-3分钟。

    https://sentry.io/for/javascript/

    https://docs.sentry.io/clients/javascript/

    https://sentry.io/h5-f5/javascript-co/getting-started/javascript-vue/

     

    保存自动上传代码到服务器:sublime - sftp插件

    http://blog.csdn.net/pheona1990/article/details/52092799

     

    css table 行内编辑神奇:x-editable

    http://vitalets.github.io/x-editable/docs.html#select

     

    将页面上所有的readonly-input修改为可编写

    document.querySelectorAll("input[readonly]").forEach(function(item, i){    item.removeAttribute('readonly');});

     

    surface 4 pro 上玩神武窗口太小的问题

    1、先将游戏缩下去
    2、将缩放比调为200,再调为300(最大)即可
    3、如果上面两步还不行,就在开一个客户端神武3试试。

      

    淘宝手机适配解决方案

    https://github.com/amfe/lib-flexible/tree/master

     

    window默认的cmd显示中文为????问题:

    配置 -> “字体” -> “Lucida Console” 
    重启cmd即可显示中文

    还有一种情况是,要设置为中文
    https://jingyan.baidu.com/article/cb5d610535c4ed005c2fe088.html

     

    强大的上传插件,支持停止

    https://github.com/moxiecode/plupload

      https://github.com/moxiecode/plupload/edit/master/js/moxie.min.js
      https://github.com/moxiecode/plupload/blob/master/js/plupload.min.js

      https://github.com/moxiecode/plupload/tree/master/examples

     

    优惠券领取

    http://www.quanmama.com/quan/2111914.html

     

    4天搞定考试!2017证券从业资格考试直播串讲冲刺课

    https://www.bilibili.com/video/av15897093/

     

    万物皆可萌,使用可萌记账管理你的财务 

    http://www.kemengjizhang.com/

      

    滴答清单:来构造自己的TODO LIST (推荐使用QQ登录,方便PC和移动端)

    https://www.dida365.com/

     

    Total Commander 高效管理文件 

    http://rj.baidu.com/soft/detail/16892.html?ald

      

    用foxmail 高效管理email(如QQ邮箱和公司邮箱) 

    # foxmail绑定QQ邮箱,注意如果有密码保护要填写密码保护
    http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=371

    http://rj.baidu.com/soft/detail/14579.html?ald

      

    考虑使用Postman的单元测试。非常强大。弥补了我对单元测试理解中的缺陷。928532756@qq.com

    https://www.getpostman.com/
    https://go.pstmn.io/docs-test-scripts

       

    laravel教程,主要学习别人是怎么写文章和学习的

    https://github.com/johnlui/Learn-Laravel-5/issues/16

     

    windows下make 命令的使用和下载。下载之后放置在%PATH%中,使用命令行make -v即可查看到相关信息

    http://www.equation.com/servlet/equation.cmd?fa=make

     

    偶然获得的腾讯学堂 

    https://ke.qq.com/user/index/index.html#cid=230866&tid=100272363&term_id=100272363

     

    公司网络的配置

     

     

    QQ聊天记录摘抄正则表达式 

    <tr.*星辰.*</tr>$

      

    gitbook编译成网页

    # 安装全局gitbook编译工具,下载不了的话请使用cnpm
    npm install gitbook-cli -g
    # 要在gitbook项目中编译才有效。生成为_book的文件夹就是了 gitbook build

     

     

    录屏软件推荐:Camtasia Studio 

    https://www.techsmith.com/video-editor.html

     

    ATOM的牛逼炫酷打字插件:activate-power-mode 

    https://atom.io/packages/activate-power-mode
    https://github.com/codeinthedark/awesome-power-mode

     

     

    一款不错的快速nodejs框架:fastify 

    https://github.com/fastify/fastify

     

     

    新的无界面UI自动化工具:Puppeteer 

    https://mp.weixin.qq.com/s?__biz=MzIwNjQwMzUwMQ==&mid=2247485516&idx=1&sn=c28826ae1d5d85807bac6aa537903ca2&chksm=97236a8ea054e3981879aa82a30fed678162b8596e264fb0b805d07e6a25c1f2c0c5e5396a19&mpshare=1&scene=23&srcid=1021M8Qa0VXJUaZwJCECGTPc#rd

     

    fq 时,能上YouTube,但不能上google的原因?

    # 加ncr可以防止跳到非host指定的服务器
    http://www.google.com/ncr
    
    # 下次还是直接访问YouTube来测试吧
    https://www.youtube.com/

     

    免费小巧的录屏软件: LICEcap 

    https://www.cockos.com/licecap/

      

    免费电子书 

    https://github.com/EbookFoundation/free-programming-books/blob/master/free-programming-books-zh.md

      

    phpStudy安装redis扩展 

    # 教程
    http://blog.csdn.net/tianjingang1/article/details/68491369

    # php_redis.dll资源
    http://windows.php.net/downloads/pecl/releases/redis/2.2.7/

    # php_igbinary.dll 资源
    http://pecl.php.net/package/igbinary

     

     chrome插件 Sourcegraph for GitHub: github 学习源码的利器

    也可以通过chrome商店搜索github安装

    https://about.sourcegraph.com/

      

    apiary: 所见即所得的API编辑器。并且提供POSTman工具和各语言请求代码生成工具。左边是markdown,右边是控制台。 

    https://app.apiary.io

      

    轻量级桌面应用开发的捷径——nw.js。 请务必下载sdk版本,才可以打开F12 chroe调试工具

    # github:
    https://github.com/nwjs/nw.j

     # 官网:

     https://nwjs.io/ 

     

    抽空可以学一下,简单的js代码 + canvas 生成字母雨

    <script src="//cdn.batsing.com/matrix.js"></script>
    <canvas id="matrixBG" ></canvas>

     

    为博客园编制一个好看的皮肤

    1、进入管理页面
    2、选项
    3、博客设置
    4、博客皮肤选择CodingLife
    5、页面定制css代码中加入:http://www.cnblogs.com/trlanfeng/p/5343935.html
    6、勾选“禁用模板默认css”
    7、保存

     

    window下默认安装的git是不会自动加入到PATH中的,以防万一还是尽快加入一下。

    以免一些需要依赖git的命令如bower就会报:“Bower : ENOGIT git is not installed or not in the PATH”

    # 找到git的bin目录,加入到PATH中,如笔者的:
    C:\Program Files\Git\bin

     

    NPM 查看模块的版本列表

    npm view bootstrap versions

     

    像素大厨pxcook: 快速把pdf改为css、Android、IOS代码。很类似标你妹

    http://www.fancynode.com.cn/pxcook

    # 同类产品,快速切图
    http://www.fancynode.com.cn/bigshear

     

    免费在线作图,实时协作

    https://www.processon.com/

     

    用js开发桌面应用

    https://electron.atom.io/

     

    一个神奇的Linux视频学习地址

    http://www.watchmen.cn/portal.php?mod=view&aid=22

      

    仙剑下载地址

    http://www.paopaoche.net/yx/xianjian/

     

    JS查看元素指定事件(如click)绑定的函数

    http://sudodev.cn/detect-jquery-event-function-define/

     

    蝙蝠侠漫画下载

    https://site.douban.com/137075/room/1200579/

    豆瓣网,搜索时点击“小站”,然后输入“”。进这个小站以后点击“Watchtower”。然后往下滑之后会看见“漫画下载”,点击旁边的“全部”,这里就是每周更新的漫画下载的地方。你自己还可以探索这个小站。 

      

    67 个拯救前端开发者的工具、库和资源

    https://juejin.im/entry/59b202e96fb9a0249b487fd5?utm_source=gold_browser_extension

     

    VS 编辑器 如何修改项目文件夹

    http://blog.csdn.net/zzq900503/article/details/8777856

     

    不错的外设购买地址

    https://item.jd.com/10494209225.html

     

    云端真机测试

    # testin 众测
    https://www.testin.cn/business/machine.htm
    
    # 百度MTC测试平台
    http://mtc.baidu.com/tinypace/main#/

     

     

    腾讯问卷:靠谱的第三方“投票”、“问卷”的生成器

    https://wj.qq.com/

     

    npm升级到最新

    npm install npm@latest -g

     

    《Apache Cordova 开发指南》

    https://waylau.gitbooks.io/cordova-dev-guide/docs/getstarted.html

     

    window下,让系统变量、环境变量修改之后不重启而立即生效的方法

    打开cmd命令行,再关闭即可。就这么简单

     

    游戏王高清片源

    http://www.bilibili.com/video/av3277237/index_138.html#page=5

      

    比较安全且便宜的mac购买地方

    https://item.taobao.com/item.htm?spm=a1z10.3-c.w4002-15025288938.22.3ff19e11O6xnY5&id=520817696604

     

    如何制作一个完美的全屏视频H5

    https://mp.weixin.qq.com/s?__biz=MzU0OTExNzYwNg==&mid=2247483736&idx=1&sn=58f78c41fb36996a6f8255454095bc66&chksm=fbb58a91ccc203876cfbf8a2f7ff03334dad0027d78002d63b4a2461a4b53c90e03fbd5aa7f4&scene=0&key=1be27594fc89b279f5984d9be1944fd804d78a6340fccc5a7c3539db275cbd9e7e58350b84ecbba15221756ba796b8f53a60216759f019aaa5447b637db8b995212dc9ad288afc4e92c0464637b88abb&ascene=0&uin=MjIxODIxNjA0MA==&devicetype=iMac%20MacBookPro12,1%20OSX%20OSX%2010.11.6%20build(15G1004)&version=12010110&nettype=WIFI&fontScale=100&pass_ticket=TF0DDsNPjig2m8CaC8KMtlBHOkI0E09bIxI2OdnmkisHZvWOlOoCBPbVACKyxIeu

       

    github的高级搜索技巧

    http://blog.csdn.net/laokdidiao/article/details/51442835
    https://segmentfault.com/q/1010000003491604
    http://blog.csdn.net/shuimuzy/article/details/55506706

     

    学会如何阅读源码

    http://blog.csdn.net/liuer2004_82/article/details/51260662
    http://blog.csdn.net/laokdidiao/article/details/51402242

     

    连jQuery之父John Resig 都膜拜的大神Jennifer Dewalt:《180天180个网站》 , 这妹子可以的!

    # 官网
    https://jenniferdewalt.com/
    
    # github
    https://github.com/jendewalt/jennifer_dewalt

    # 好玩的
    https://jenniferdewalt.com/electro_bounce/page
    https://jenniferdewalt.com/audio_garden/gardens
    https://jenniferdewalt.com/mousing/page
    https://jenniferdewalt.com/minesweeper/game
    https://jenniferdewalt.com/what_day/page
    https://jenniferdewalt.com/lights_on/game
    https://jenniferdewalt.com/letter_storm/game
    https://jenniferdewalt.com/portrait/photos/new
    https://jenniferdewalt.com/audio_recorder/page

     

    在window下使用make命令: Cmake

    https://cmake.org/download/

     

    .net framework 4.62升级补丁

    http://dotnetsocial.cloudapp.net/GetDotnet?tfm=.NETFramework,Version=v4.6.2

      

    目前我见过最好的Loading制作平台

    https://loading.io/

    # bounce / bounce around
    https://loading.io/animation/icon/27506
    https://loading.io/spinner/

      

    团队API + mock丑陋的平台

    http://thx.github.io/RAP/

     

    最新的前端测试框架Jest。facebook出的哦

    https://github.com/facebook/jest

     

    h5头像上传裁剪的插件

    https://github.com/fengyuanchen/cropper

     

    Redis 桌面工具

    https://github.com/uglide/RedisDesktopManager

      

    可视化mongodb :robomongo

    # 请下载Robo 3T
    https://robomongo.org/

      

    软件版postman下载

    https://www.getpostman.com/

     

    我最喜欢也是最强大的模板引擎Jade(pug). 不仅仅Nodejs版本,还有其它如java、ruby、php等版本可以使用

    # github
    https://github.com/pugjs/pug
    
    # 官网与API
    https://pugjs.org/language/inheritance.html

     

    window环境下,一个npm命令解决所有环境问题

    解决了我多年node-sass安装失败的问题.也包括以后的各种npm环境依赖问题

    安装完成之后,可以通过npm rebuild node-sass 来测试是否正常。如果还是不正常。那可能需要你手动安装一下buildTool_Full.exe

    大概目录位置在C:\Users\用户名\.windows-build-tools\BuildTools_Full.exe. 如果你电脑上已经有VS系列,那可能需要先删除,才可以使用了

    # github地址
    https://github.com/felixrieseberg/windows-build-tools
    
    # 安装命令(使用管理员运行powershell / cmd运行以下代码)
    npm install --global --production windows-build-tools

     

    团贷网的Android使用的jsBridge库

    # github 库
    https://github.com/lzyzsd/JsBridge

    # 教程
    http://www.cnblogs.com/whoislcj/p/6104015.html

      

    pdf下载好去处:微盘 

    http://vdisk.weibo.com/

      

    weflow:一个高效、强大、跨平台的前端开发工作流工具

    https://weflow.io/

      

    HTML5拖放API Drag and Drop

    https://segmentfault.com/a/1190000010127530?v=20170710

     

    7月份前端资源分享

    https://segmentfault.com/a/1190000010121230

     

    跨域还可以这样玩,使用CSS3特性做跨域

    http://alili.tech/2017/06/20/Javascript/CSST%20(CSS%20Text%20Transformation)%20%E4%BD%BF%E7%94%A8CSS3%E7%89%B9%E6%80%A7%E5%81%9A%E8%B7%A8%E5%9F%9F/

     

    JavaScript 错误监控平台

    https://fundebug.com/

     

    可运行后端(java、python、Node.js、swift/Object-C、Go、C/C++/C#、PHP等)程序的在线开发环境: 

    并不一定要自己买房子以后才有地方住。并不一定要在本机安装开发环境以后,才能运行第一个程序
    # 可运行后端语言代码
    http://ideone.com/

    # 在线编辑、展示、分享、交流你的 html + css + javascript 代码
    http://runjs.cn/

    # 在线编写es6+的代码平台
    http://babeljs.io/repl/

     

    5分钟打造简易高效的webpack配置

    https://juejin.im/post/595a0ed86fb9a06ba6463cd3?utm_source=gold_browser_extension

     

    Node连接Mysql遇到的坑以及踩坑总结

    https://segmentfault.com/a/1190000009676923

     

    基于node+express api接口数据 

    https://ecitlm.github.io/SpliderApi/#/

      

    思维导图在线制作的好地方:百度脑图

    http://naotu.baidu.com/

     

    最全正则表达式总结:验证QQ号、手机号、Email、中文、邮编、身份证、IP地址等

    http://www.lovebxm.com/2017/05/31/RegExp/

     

      

    简单、统一、轻巧的 Node.js 版网易云音乐 API 库

    https://juejin.im/entry/5946b695128fe1006a48643f/detail?utm_source=gold_browser_extension

     

    第三方音频播放资源搜集

    https://github.com/jplayer/jPlayer
    
    (非常简洁,推荐这个)
    http://kolber.github.io/audiojs/
    
    https://github.com/marcj/css-element-queries
    
    https://github.com/scottschiller/SoundManager2

      

    最全的加载动画网站 

    https://preloaders.net/

      

    微信小程序开源项目库汇总 

    https://github.com/opendigg/awesome-github-wechat-weapp

      

    上传图片的解决方案 

    第三方github插件:http://www.jq22.com/yanshi11638
    github插件(强烈推荐):https://github.com/think2011/localResizeIMG
    github大型插件:https://github.com/blueimp/jQuery-File-Upload
    第三方作者提供的源码(推荐阅读):    http://www.cnblogs.com/moqiutao/p/6279905.html

      

    wikiHow是一项协作项目,目标是建立世界最大的最高质量的指导手册。

    http://www.wikihow.com/Main-Page

     

    『为移动端而生』的自定义多级联动选择器

    # 介绍
    https://juejin.im/post/583faa050ce46300576216df

    # github
    https://github.com/AppianZ/multi-picker

    # china city json
    https://github.com/AppianZ/multi-picker/blob/master/MultiPicker/city.js

     

    webpack2.0中文手册 + 多个实用demo

    # 2.0版本的文档
    http://www.css88.com/doc/webpack2/plugins/extract-text-webpack-plugin/

    # 最新版本的文档
    http://www.css88.com/doc/webpack/

     

    趣味console

    https://github.com/yy0608/console

     

    脚手架网站yeoman-generators

    http://yeoman.io/generators/

     

    移动端几K仿真时间选择器

    https://xingchou.github.io/pluginApp/dist/index.html#/list/datePicker

      

    URL参数操作方法大全

    https://juejin.im/post/59424089ac502e006b7722a1?utm_source=gold_browser_extension

     

    极简的代码实现多态的表格: GridManager 

    https://github.com/baukh789/GridManager

     

    vueg:使vue-router具备转场效果

    https://github.com/jaweii/vueg

     

    window下最好的cmd神器:cmder

    # 中文显示乱码问题,建议在Settings>Startup>Environment中添加set LC_ALL=zh_CN.UTF8
    http://cmder.net/

     

    github的使用技巧,要学习一样新技术、新框架时,先往github搜索 “Awesome 关键词” , 如 “Awesome vue”、 "Awesome Django"等

    https://github.com/vuejs/awesome-vue

      

    社区策划的知识图表,用于学习任何东西的最佳路径

    https://learn-anything.xyz/

     

    动画渐变函数图示

    http://easings.net/zh-cn 

     

    js bridge -DSBridge2.0

    https://juejin.im/post/593fa055128fe1006aff700a?utm_source=gold_browser_extension

     

    Leetcode刷题 

    https://leetcode.com/problems/

    # 持续更新:用 JavaScript 刷 LeetCodeOJ 解题报告合集
    https://github.com/hijiangtao/LeetCodeOJ

     

    最喜欢的alert组件

    http://t4t5.github.io/sweetalert/

     

    火狐浏览器各大历史版本下载

    http://ftp.mozilla.org/pub/firefox/releases/

     

    非常专业的 JavaScript 分类库大全集锦

    http://www.javascript.fun/library

     

    1kb代码搞定开发调试发布,错误监控上报,用户问题定位 

    https://github.com/AlloyTeam/AlloyLever

     

    使用flv.js做直播

    https://github.com/gwuhaolin/blog/issues/3

     

    微信调试,各种WebView样式调试、手机浏览器的页面真机调试。便捷的远程调试手机页面、抓包工具,支持:HTTP/HTTPS,无需USB连接设备。

    https://github.com/wuchangming/spy-debugger

     

    移动端浏览器调试方法汇总

    http://elevenbeans.github.io/2017/06/06/移动端浏览器调试方法汇总/

     

    CHROME开发者工具的小技巧

    http://coolshell.cn/articles/17634.html

     

    Chrome 开发者工具中的命令菜单:(ctrl + shift + i ) + (ctrl + shift + p)

    https://loveky.github.io/2017/06/06/chrome-devtools-command-menu/

     

    2017 年用于 UI 设计的 CSS3 和 JavaScript 动画库

    http://www.css88.com/archives/7389

     

    pix2code - 通过训练的深度神经网络,从截图直接生成 UI 代码

    https://github.com/tonybeltramelli/pix2code

     

    Windows下用tree命令生成目录树

    tree /F > readme.md

     

    AlloyTouch丝般顺滑的触摸运动方案

    https://github.com/AlloyTeam/AlloyTouch

     

    掘金浏览器插件快速安装

    https://juejin.im/extension

     

    打字效果的实现

    # 官网
    http://www.mattboldt.com/demos/typed-js/
    
    # github
    https://github.com/mattboldt/typed.js/

     

    全栈开发实例:如何独立开发并且发布一个 WebAPP

    https://juejin.im/entry/591d4123a0bb9f005f207c31

     

     
    最好的用户交互的设计工具(mac):sketch
    https://www.sketchapp.com/

     

    码云,中国版本的github。又快又舒服又友好
    http://git.oschina.net/cyleeCat

     

    事件定位和源码查找
    http://www.xuechenlei.com/2016/09/browser-debugging-event-location-source-search/
      

    从零到一:用Phaser.js写意地开发小游戏

    https://segmentfault.com/a/1190000009212221

     

    IntelliJ IDEA 下载地址与使用方法

    pan.baidu.com/s/1jHHAHQe

    http://www.hishenyi.com/archives/1051

     

    Animate.css Chrome浏览器插件,快速生成独立的css代码

    https://www.qcloud.com/community/article/920462?fromSource=gwzcw.114070.114070.114070
    
    https://chrome.google.com/webstore/detail/animate-playerbox/lpjcokgibjaiedlkgjlkplfdppmehbeb

     

    facebook又搞事情:使用prepack工具优化你的js代码

    https://prepack.io/

     

    easy-mock在线mock平台 

    https://www.easy-mock.com/

      

    lodash.js最强大的工具库, Understore.js的代替品 

    https://github.com/lodash/lodash

     

    babel-polyfill 按需引入的es修补匠 

    https://github.com/zloirock/core-js

     

    新的动画工具库:TweenMax/greensock

    https://greensock.com/examples-showcases
    https://github.com/greensock/GreenSock-JS

      

    神器!解放你的双手——UI 设计稿全自动切图和标注的一些工具推荐

    https://juejin.im/entry/58ee01f85c497d0062d0471e

      

    前端新社区

    http://www.css88.com/
    
    http://www.css88.com/webkit/-webkit-appearance/

     

    一个不错的后台UI模板

    http://dreamsky.github.io/main/blog/metronic-bootstrap/metronic.bootstrap.rar

     

    下拉菜单插件:superfish 

    https://github.com/joeldbirch/superfish

     

    fullpage.js 优秀的demo集合 

    http://download.csdn.net/download/qianqianyixiao1/8946235

     

     Cute.Slider.js 一个优秀华丽的3D轮播器 

    http://download.csdn.net/download/aimeet/5055376

      

    ELF——面向开发者的灵活可扩展的 HTML5 构建工具 

    https://elf.aotu.io/

     

    发现更多有趣的js

    # github
    https://github.com/microjs/microjs.com
    
    # 官网列表
    http://microjs.com/#

     

    技术类pdf下载常用地址集合

    # csdn 图书下载
    http://download.csdn.net/book_list

    # 万千合集站:又一个野生的pdf学习资料下载优秀资源网站
    http://www.hejizhan.com/

    # 搜百度云资源网:比较靠谱的pdf学习资料搜索和下载渠道

    http://www.sobaidupan.com/file-89802364.html

    # safari图书在线
    https://www.safaribooksonline.com/

    # SitePoint图书下载
    https://www.sitepoint.com/premium/

     

    一个简单友好的书签气泡引导用户添加桌面快捷方式 

    https://code.google.com/archive/p/mobile-bookmark-bubble/source/default/source

     

    js实现复制到剪贴板功能,兼容所有浏览器

    http://www.cnblogs.com/PeunZhang/p/3324727.html

       

    css线性渐变特效生成器 

    http://www.colorzilla.com/gradient-editor/

     

     一个开源的项目Mobile Boilerplate 

    - 该项目的目的是为移动设备的前端开发提供一个文件的基础模板 

    #移动端版本
    https://github.com/h5bp/mobile-boilerplate/tree/v4.1.2

    #PC端版本
    https://github.com/h5bp/html5-boilerplate

    #生成各式ico图片的工具
    http://ydimage.yidianhulian.com/

       

    静态模板大全下载 

    12部分,此为9. 第12部分下载http://download.csdn.net/detail/dehua2217/9053105;
    第11部分下载http://download.csdn.net/detail/dehua2217/9064309
    第10部分下载http://download.csdn.net/detail/dehua2217/9064333
    第8部分下载http://download.csdn.net/detail/dehua2217/9052939
    第7部分下载http://download.csdn.net/detail/dehua2217/9052909
    第6部分下载http://download.csdn.net/detail/dehua2217/9052879
    第5部分下载http://download.csdn.net/detail/dehua2217/9052831
    第4部分下载http://download.csdn.net/detail/dehua2217/9052801
    第3部分下载http://download.csdn.net/detail/dehua2217/9052653
    第2部分下载http://download.csdn.net/detail/dehua2217/9052567
    第1部分下载http://download.csdn.net/detail/dehua2217/9052513

        

    滚动监听waypoints,通常配合animate.css 实现效果

    https://github.com/imakewebthings/waypoints

    http://dtop.powereasy.net/Item/3833.aspx

      

    bootstrap定制 

    http://v3.bootcss.com/customize/

      

    Tabulator 是一个易用的交互式 jQuery 表单生成插件。

    http://olifolkerd.github.io/tabulator/

     

    easyIco 最完美的 图标库

    http://www.easyicon.net/

      

    《一些超赞的loading解决方案》

    https://zhuanlan.zhihu.com/p/24464355?refer=itlion114

      

    《Mobiscroll》- 一款强大的UI组件框架

    https://github.com/acidb/mobiscroll

     

    《CSS3 在线制作资源》

    如果你对CSS3不熟悉,你可以借助http://cssarrowplease.com 网站来帮你制作一些箭头和对话框;
    
    借助http://css3buttongenerator.com 来帮助你制作CSS的按钮(Button);
    
    借助http://css3generator.com 来帮助你制作一些其他效果。

      

    《frameword7 -- Full featured HTML framework for building iOS & Android apps 》

    https://github.com/nolimits4web/Framework7

      

    《Web前端从入门菜鸟到实践老司机所需要的资料与指南合集》

    https://segmentfault.com/a/1190000007611188

     

    《tinypng图片压缩美化工具-网页版》

    https://tinypng.com/

      

    《Autoprefixer CSS》 自动为css添加浏览器厂商前缀

    https://autoprefixer.github.io/ 

     

    gitlab:企业公司版本使用的github

    http://gitlab.weicantimes.com:8000/users/sign_in

         

    《touch手势库》 

    https://github.com/hammerjs/hammer.js

    https://github.com/AlloyTeam/AlloyFinger

         

    2016 年最好用的表单验证库 SMValidator.js 

    https://github.com/WLDragon/SMValidator

    https://wldragon.github.io/SMValidator/tutor/tutor1.html

      

    《利用HTML5分片上传超大文件》 

    http://www.tuicool.com/articles/nuAvm22

      

    《css解决方案大全:代码库,代码块,常用布局》 

    http://nec.netease.com/

         

    vue-table 

    https://github.com/ratiw/vue-table

    https://github.com/ratiw/vuetable-2

         

    mock.js-让前端独立于后端进行开发 

    https://github.com/nuysoft/Mock

    http://mockjs.com/

       

    <WOW.JS> - 实现滚动动画 

    https://github.com/matthieua/WOW

       

    《轻松做一个API document、book :gitbook + GitBook Editor》 

    https://www.gitbook.com/@dragon8github/dashboard

    https://www.gitbook.com/editor/

      

    免费的后台 、后端

    http://www.bmob.cn/

      

    免费的API数据 

    https://www.juhe.cn/docs/index/otherid/1

     

      实验楼,实践是最好的学习

    https://www.shiyanlou.com/courses/

      

    《色值转换工具》

     http://www.atool.org/colorpicker.php

     

    你不需要jquery

    http://youmightnotneedjquery.com/

     

    暗黑破坏神下载

    http://diablo2.anhei3.net/d2113.html
    
    https://pan.baidu.com/s/1kV2ONSr#list/path=%2F%E6%9A%97%E9%BB%91%E7%A0%B4%E5%9D%8F%E7%A5%9E%E7%9B%B8%E5%85%B3%E6%96%87%E4%BB%B6&parentPath=%2F

     

    bootstrap可视化布局,比官方的还全面一些

    http://www.hishenyi.com/bootstrap/

     

    看漫画的好去处:acgmoon 寂月神社

    https://www.jiyue.com/

     

     据说这个网站的安全性和防御dos攻击可以和Twitter,facebook,美国国家安全局的官网媲美:  

    http://www.pornhub.com/  
    cctxer
    jiongtucc 
    119.23.22.136 mP
    icound: 正常mp
    微软:mpP
    evernode(印象笔记)sb
    钉钉:sb
    laravel-china: 它的QQ号码,laravelcc

     

    转载于:https://www.cnblogs.com/CyLee/p/5612202.html

    展开全文
  • 点击上方“开发者技术前线”,选择“星标”13:21 在看真爱作者:mcuking(杭州个推)https://juejin.im/post/5d759f706fb9a06afa32ade...

    点击上方“开发者技术前线”,选择“星标”

    13:21 在看 真爱

    作者:mcuking(杭州个推)

    https://juejin.im/post/5d759f706fb9a06afa32adec

    笔者在公司用 web 技术开发移动端应用已经有一年多的时间了,开始主要以 vue 技术栈配合 native 为主,目前演进成 vue + react native 技术架构,vue 主要负责开发 OA 业务,比如报销、出差、crm 等等,react native 主要负责即时通信部分,是在 mattermost-mobile[1] 的基础上修改的(mattermost 是一个开源的即时通讯方案)。

    因为公司在这方面没有太多技术沉淀,所以在开发期间遇到了很多坑,经过一年多的技术攻克积累,最终形成了这套比较完善的解决方案,总结出来希望能够帮助到大家,尤其是对一些中小公司这方面经验不足的(PS: 大公司估计有他们自己的一套方案了)。

    好了废话不多说,先亮下这个库的 GitHub 地址,后面还会不断完善,欢迎 star:

    mobile-web-best-practice[2]

    移动端 web 最佳实践,基于 vue-cli3[3] 搭建的 typescript[4] 项目,可以用于 hybrid 应用或者纯 webapp 开发。以下大部分内容同样适用于 react[5] 等前端框架。

    其中有三个点尚在完善中:领域驱动设计(DDD)应用、微前端、性能监控,后续完成后会以单独的文章发出来。其中性能监控还没有太好的选择,类似错误监控 sentry 那种开源免费而且功能强大的工具,如果有人知道的麻烦告知下。文中难免有些错误或者更好的方案,也欢迎不吝赐教。

    目录

    • 组件库[6]

    • JSBridge[7]

    • 路由堆栈管理(模拟原生 APP 导航)[8]

    • 请求数据缓存[9]

    • 构建时预渲染[10]

    • Webpack 策略[11]

      • 基础库抽离[12]

    • 手势库[13]

    • 样式适配[14]

    • 表单校验[15]

    • 阻止原生返回事件[16]

    • 通过 UA 获取设备信息[17]

    • mock 数据[18]

    • 调试控制台[19]

    • 抓包工具[20]

    • 异常监控平台[21]

    • 常见问题[22]

    组件库

    vant[23]

    vux[24]

    mint-ui[25]

    cube-ui[26]

    vue 移动端组件库目前主要就是上面罗列的这几个库,本项目使用的是有赞前端团队开源的 vant。

    vant 官方目前已经支持自定义样式主题,基本原理就是在 less-loader[27] 编译 less[28] 文件到 css 文件过程中,利用 less 提供的 modifyVars[29] 对 less 变量进行修改,本项目也采用了该方式,具体配置请查看相关文档:

    定制主题[30]

    推荐一篇介绍各个组件库特点的文章:

    Vue 常用组件库的比较分析(移动端)[31]

    JSBridge

    DSBridge-IOS[32]

    DSBridge-Android[33]

    WebViewJavascriptBridge[34]

    混合应用中一般都是通过 webview 加载网页,而当网页要获取设备能力(例如调用摄像头、本地日历等)或者 native 需要调用网页里的方法,就需要通过 JSBridge 进行通信。

    开源社区中有很多功能强大的 JSBridge,例如上面列举的库。本项目基于保持 iOS android 平台接口统一原因,采用了 DSBridge,各位可以选择适合自己项目的工具。

    本项目以 h5 调用 native 提供的同步日历接口为例,演示如何在 dsbridge 基础上进行两端通信的。下面是两端的关键代码摘要:

    安卓端同步日历核心代码,具体代码请查看与本项目配套的安卓项目 mobile-web-best-practice-container[35]

    public class JsApi {    /**     * 同步日历接口     * msg 格式如下:     * ...     */    @JavascriptInterface    public void syncCalendar(Object msg, CompletionHandler<Integer> handler) {        try {            JSONObject obj = new JSONObject(msg.toString());            String id = obj.getString("id");            String title = obj.getString("title");            String location = obj.getString("location");            long startTime = obj.getLong("startTime");            long endTime = obj.getLong("endTime");            JSONArray earlyRemindTime = obj.getJSONArray("alarm");            String res = CalendarReminderUtils.addCalendarEvent(id, title, location, startTime, endTime, earlyRemindTime);            handler.complete(Integer.valueOf(res));        } catch (Exception e) {            e.printStackTrace();            handler.complete(6005);        }    }}
    

    h5 端同步日历核心代码(通过装饰器来限制调用接口的平台)

    class NativeMethods {  // 同步到日历  @p()  public syncCalendar(params: SyncCalendarParams) {    const cb = (errCode: number) => {      const msg = NATIVE_ERROR_CODE_MAP[errCode];      Vue.prototype.$toast(msg);      if (errCode !== 6000) {        this.errorReport(msg, 'syncCalendar', params);      }    };    dsbridge.call('syncCalendar', params, cb);  }  // 调用 native 接口出错向 sentry 发送错误信息  private errorReport(errorMsg: string, methodName: string, params: any) {    if (window.$sentry) {      const errorInfo: NativeApiErrorInfo = {        error: new Error(errorMsg),        type: 'callNative',        methodName,        params: JSON.stringify(params)      };      window.$sentry.log(errorInfo);    }  }}/** * @param {platforms} - 接口限制的平台 * @return {Function} - 装饰器 */function p(platforms = ['android', 'ios']) {  return (target: AnyObject, name: string, descriptor: PropertyDescriptor) => {    if (!platforms.includes(window.$platform)) {      descriptor.value = () => {        return Vue.prototype.$toast(          `当前处在 ${window.$platform} 环境,无法调用接口哦`        );      };    }    return descriptor;  };}
    

    另外推荐一个笔者之前写的一个基于安卓平台实现的教学版 JSBridge[36],里面详细阐述了如何基于底层接口一步步封装一个可用的 JSBridge:

    JSBridge 实现原理[37]

    路由堆栈管理(模拟原生 APP 导航)

    vue-page-stack[38]

    vue-navigation[39]

    vue-stack-router[40]

    在使用 h5 开发 app,会经常遇到下面的需求:从列表进入详情页,返回后能够记住当前位置,或者从表单点击某项进入到其他页面选择,然后回到表单页,需要记住之前表单填写的数据。可是目前 vue 或 react 框架的路由,均不支持同时存在两个页面实例,所以需要路由堆栈进行管理。

    其中 vue-page-stack 和 vue-navigation 均受 vue 的 keepalive 启发,基于 vue-router[41],当进入某个页面时,会查看当前页面是否有缓存,有缓存的话就取出缓存,并且清除排在他后面的所有 vnode,没有缓存就是新的页面,需要存储或者是 replace 当前页面,向栈里面 push 对应的 vnode,从而实现记住页面状态的功能。

    而逻辑思维前端团队的 vue-stack-router 则另辟蹊径,抛开了 vue-router,自己独立实现了路由管理,相较于 vue-router,主要是支持同时可以存活 A 和 B 两个页面的实例,或者 A 页面不同状态的两个实例,并支持原生左滑功能。但由于项目还在初期完善,功能还没有 vue-router 强大,建议持续关注后续动态再做决定是否引入。

    本项目使用的是 vue-page-stack,各位可以选择适合自己项目的工具。同时推荐几篇相关文章:

    【vue-page-stack】Vue 单页应用导航管理器 正式发布[42]

    Vue 社区的路由解决方案:vue-stack-router[43]

    请求数据缓存

    mem[44]

    在我们的应用中,会存在一些很少改动的数据,而这些数据有需要从后端获取,比如公司人员、公司职位分类等,此类数据在很长一段时间时不会改变的,而每次打开页面或切换页面时,就重新向后端请求。为了能够减少不必要请求,加快页面渲染速度,可以引用 mem 缓存库。

    mem 基本原理是通过以接收的函数为 key 创建一个 WeakMap,然后再以函数参数为 key 创建一个 Map,value 就是函数的执行结果,同时将这个 Map 作为刚刚的 WeakMap 的 value 形成嵌套关系,从而实现对同一个函数不同参数进行缓存。而且支持传入 maxAge,即数据的有效期,当某个数据到达有效期后,会自动销毁,避免内存泄漏。

    选择 WeakMap 是因为其相对 Map 保持对键名所引用的对象是弱引用,即垃圾回收机制不将该引用考虑在内。只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。

    mem 作为高阶函数,可以直接接受封装好的接口请求。但是为了更加直观简便,我们可以按照类的形式集成我们的接口函数,然后就可以用装饰器的方式使用 mem 了(装饰器只能修饰类和类的类的方法,因为普通函数会存在变量提升)。下面是相关代码:

    import http from '../http';import mem from 'mem';/** * @param {MemOption} - mem 配置项 * @return {Function} - 装饰器 */export default function m(options: AnyObject) {  return (target: AnyObject, name: string, descriptor: PropertyDescriptor) => {    const oldValue = descriptor.value;    descriptor.value = mem(oldValue, options);    return descriptor;  };}class Home {  @m({ maxAge: 60 * 1000 })  public async getUnderlingDailyList(    query: ListQuery  ): Promise<{ total: number; list: DailyItem[] }> {    const {      data: { total, list }    } = await http({      method: 'post',      url: '/daily/getList',      data: query    });    return { total, list };  }}export default new Home();
    

    构建时预渲染

    针对目前单页面首屏渲染时间长(需要下载解析 js 文件然后渲染元素并挂载到 id 为 app 的 div 上),SEO 不友好(index.html 的 body 上实际元素只有 id 为 app 的 div 元素,真正的页面元素都是动态挂载的,搜索引擎的爬虫无法捕捉到),目前主流解决方案就是服务端渲染(SSR),即从服务端生成组装好的完整静态 html 发送到浏览器进行展示,但配置较为复杂,一般都会借助框架,比如 vue 的 nuxt.js[45],react 的 next[46]

    其实有一种更简便的方式--构建时预渲染。顾名思义,就是项目打包构建完成后,启动一个 Web Server 来运行整个网站,再开启多个无头浏览器(例如 Puppeteer[47]Phantomjs[48] 等无头浏览器技术)去请求项目中所有的路由,当请求的网页渲染到第一个需要预渲染的页面时(需提前配置需要预渲染页面的路由),会主动抛出一个事件,该事件由无头浏览器截获,然后将此时的页面内容生成一个 HTML(包含了 JS 生成的 DOM 结构和 CSS 样式),保存到打包文件夹中。

    根据上面的描述,我们可以其实它本质上就只是快照页面,不适合过度依赖后端接口的动态页面,比较适合变化不频繁的静态页面。

    实际项目相关工具方面比较推荐 prerender-spa-plugin[49] 这个 webpack 插件,下面是这个插件的原理图。不过有两点需要注意:

    一个是这个插件需要依赖 Puppeteer,而因为国内网络原因以及本身体积较大,经常下载失败,不过可以通过 .npmrc 文件指定 Puppeteer 的下载路径为国内镜像;

    另一个是需要设置路由模式为 history 模式(即基于 html5 提供的 history api 实现的,react 叫 BrowserRouter,vue 叫 history),因为 hash 路由无法对应到实际的物理路由。(即线上渲染时 history 下,如果 form 路由被设置成预渲染,那么访问 /form/ 路由时,会直接从服务端返回 form 文件夹下的 index.html,之前打包时就已经预先生成了完整的 HTML 文件 )

    本项目已经集成了 prerender-spa-plugin,但由于和 vue-stack-page/vue-navigation 这类路由堆栈管理器一起使用有问题(原因还在查找,如果知道的朋友也可以告知下),所以 prerender 功能是关闭的。

    同时推荐几篇相关文章:

    vue 预渲染之 prerender-spa-plugin 解析(一)[50]

    使用预渲提升 SPA 应用体验[51]

    Webpack 策略

    基础库抽离

    对于一些基础库,例如 vue、moment 等,属于不经常变化的静态依赖,一般需要抽离出来以提升每次构建的效率。目前主流方案有两种:

    一种是使用 webpack-dll-plugin[52] 插件,在首次构建时就讲这些静态依赖单独打包,后续只需引入早已打包好的静态依赖包即可;

    另一种就是外部扩展 Externals[53] 方式,即把不需要打包的静态资源从构建中剔除,使用 CDN 方式引入。下面是 webpack-dll-plugin 相对 Externals 的缺点:

    1. 需要配置在每次构建时都不参与编译的静态依赖,并在首次构建时为它们预编译出一份 JS 文件(后文将称其为 lib 文件),每次更新依赖需要手动进行维护,一旦增删依赖或者变更资源版本忘记更新,就会出现 Error 或者版本错误。

    2. 无法接入浏览器的新特性 script type="module",对于某些依赖库提供的原生 ES Modules 的引入方式(比如 vue 的新版引入方式)无法得到支持,没法更好地适配高版本浏览器提供的优良特性以实现更好地性能优化。

    3. 将所有资源预编译成一份文件,并将这份文件显式注入项目构建的 HTML 模板中,这样的做法,在 HTTP1 时代是被推崇的,因为那样能减少资源的请求数量,但在 HTTP2 时代如果拆成多个 CDN Link,就能够更充分地利用 HTTP2 的多路复用特性。

    不过选择 Externals 还是需要一个靠谱的 CDN 服务的。

    本项目选择的是 Externals,各位可根据项目需求选择不同的方案。

    更多内容请查看这篇文章(上面观点来自于这篇文章):

    Webpack 优化——将你的构建效率提速翻倍[54]

    手势库

    hammer.js[55]

    AlloyFinger[56]

    在移动端开发中,一般都需要支持一些手势,例如拖动(Pan),缩放(Pinch),旋转(Rotate),滑动(swipe)等。目前已经有很成熟的方案了,例如 hammer.js 和腾讯前端团队开发的 AlloyFinger 都很不错。本项目选择基于 hammer.js 进行二次封装成 vue 指令集,各位可根据项目需求选择不同的方案。

    下面是二次封装的关键代码,其中用到了 webpack 的 require.context 函数来获取特定模块的上下文,主要用来实现自动化导入模块,比较适用于像 vue 指令这种模块较多的场景:

    // 用于导入模块的上下文export const importAll = (  context: __WebpackModuleApi.RequireContext,  options: ImportAllOptions = {}): AnyObject => {  const { useDefault = true, keyTransformFunc, filterFunc } = options;  let keys = context.keys();  if (isFunction(filterFunc)) {    keys = keys.filter(filterFunc);  }  return keys.reduce((acc: AnyObject, curr: string) => {    const key = isFunction(keyTransformFunc) ? keyTransformFunc(curr) : curr;    acc[key] = useDefault ? context(curr).default : context(curr);    return acc;  }, {});};// directives 文件夹下的 index.tsconst directvieContext = require.context('./', false, /.ts$/);const directives = importAll(directvieContext, {  filterFunc: (key: string) => key !== './index.ts',  keyTransformFunc: (key: string) =>    key.replace(/^.//, '').replace(/.ts$/, '')});export default {  install(vue: typeof Vue): void {    Object.keys(directives).forEach((key) =>      vue.directive(key, directives[key])    );  }};// touch.tsexport default {  bind(el: HTMLElement, binding: DirectiveBinding) {    const hammer: HammerManager = new Hammer(el);    const touch = binding.arg as Touch;    const listener = binding.value as HammerListener;    const modifiers = Object.keys(binding.modifiers);    switch (touch) {      case Touch.Pan:        const panEvent = detectPanEvent(modifiers);        hammer.on(`pan${panEvent}`, listener);        break;      ...    }  }};
    

    另外推荐一篇关于 hammer.js 和一篇关于 require.context 的文章:

    H5 案例分享:JS 手势框架 —— Hammer.js[57]

    使用 require.context 实现前端工程自动化[58]

    样式适配

    postcss-px-to-viewport[59]

    Viewport Units Buggyfill[60]

    flexible[61]

    postcss-pxtorem[62]

    Autoprefixer[63]

    browserslist[64]

    在移动端网页开发时,样式适配始终是一个绕不开的问题。对此目前主流方案有 vw 和 rem(当然还有 vw + rem 结合方案,请见下方 rem-vw-layout 仓库),其实基本原理都是相通的,就是随着屏幕宽度或字体大小成正比变化。因为原理方面的详细资料网络上已经有很多了,就不在这里赘述了。下面主要提供一些这工程方面的工具。

    关于 rem,阿里无线前端团队在 15 年的时候基于 rem 推出了 flexible 方案,以及 postcss 提供的自动转换 px 到 rem 的插件 postcss-pxtorem。

    关于 vw,可以使用 postcss-px-to-viewport 进行自动转换 px 到 vw。postcss-px-to-viewport 相关配置如下:

    "postcss-px-to-viewport": {  viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是375  viewportHeight: 667, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置  unitPrecision: 3,  // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)  viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw  selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名  minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值  mediaQuery: false // 媒体查询里的单位是否需要转换单位}
    

    下面是 vw 和 rem 的优缺点对比图:

    关于 vw 兼容性问题,目前在移动端 iOS 8 以上以及 Android 4.4 以上获得支持。如果有兼容更低版本需求的话,可以选择 viewport 的 pollify 方案,其中比较主流的是 Viewport Units Buggyfill[65]

    本方案因不准备兼容低版本,所以直接选择了 vw 方案,各位可根据项目需求选择不同的方案。

    另外关于设置 css 兼容不同浏览器,想必大家都知道 Autoprefixer(vue-cli3 已经默认集成了),那么如何设置要兼容的范围呢?推荐使用 browserslist,可以在 .browserslistrc 或者 pacakage.json 中 browserslist 部分设置兼容浏览器范围。因为不止 Autoprefixer,还有 Babel,postcss-preset-env 等工具都会读取 browserslist 的兼容配置,这样比较容易使 js css 兼容浏览器的范围保持一致。下面是本项目的 .browserslistrc 配置:

    iOS >= 10  //  即 iOS SafariAndroid >= 6.0 // 即 Android WebViewlast 2 versions // 每个浏览器最近的两个版本
    

    最后推荐一些移动端样式适配的资料:

    rem-vw-layout[66]

    细说移动端 经典的 REM 布局 与 新秀 VW 布局[67]

    如何在 Vue 项目中使用 vw 实现移动端适配[68]

    表单校验

    async-validator[69]

    vee-validate[70]

    由于大部分移动端组件库都不提供表单校验,因此需要自己封装。目前比较多的方式就是基于 async-validator 进行二次封装(elementUI 组件库提供的表单校验也是基于 async-validator ),或者使用 vee-validate(一种基于 vue 模板的轻量级校验框架)进行校验,各位可根据项目需求选择不同的方案。

    本项目的表单校验方案是在 async-validator 基础上进行二次封装,代码如下,原理很简单,基本满足需求。如果还有更完善的方案,欢迎提出来。

    其中 setRules 方法是将组件中设置的 rules(符合 async-validator 约定的校验规则)按照需要校验的数据的名字为 key 转化一个对象 validator,value 是 async-validator 生成的实例。validator 方法可以接收单个或多个需要校验的数据的 key,然后就会在 setRules 生成的对象 validator 中寻找 key 对应的 async-validator 实例,最后调用实例的校验方法。当然也可以不接受参数,那么就会校验所有传入的数据。

    import schema from 'async-validator';...class ValidatorUtils {  private data: AnyObject;  private validators: AnyObject;  constructor({ rules = {}, data = {}, cover = true }) {    this.validators = {};    this.data = data;    this.setRules(rules, cover);  }  /**   * 设置校验规则   * @param rules async-validator 的校验规则   * @param cover 是否替换旧规则   */  public setRules(rules: ValidateRules, cover: boolean) {    if (cover) {      this.validators = {};    }    Object.keys(rules).forEach((key) => {      this.validators[key] = new schema({ [key]: rules[key] });    });  }  public validate(    dataKey?: string | string[]  ): Promise<ValidateError[] | string | string[] | undefined> {    // 错误数组    const err: ValidateError[] = [];    Object.keys(this.validators)      .filter((key) => {        // 若不传 dataKey 则校验全部。否则校验 dataKey 对应的数据(dataKey 可以对应一个(字符串)或多个(数组))        return (          !dataKey ||          (dataKey &&            ((_.isString(dataKey) && dataKey === key) ||              (_.isArray(dataKey) && dataKey.includes(key))))        );      })      .forEach((key) => {        this.validators[key].validate(          { [key]: this.data[key] },          (error: ValidateError[]) => {            if (error) {              err.push(error[0]);            }          }        );      });    if (err.length > 0) {      return Promise.reject(err);    } else {      return Promise.resolve(dataKey);    }  }}
    

    阻止原生返回事件

    开发中可能会遇到下面这个需求:当页面弹出一个 popup 或 dialog 组件时,点击返回键时是隐藏弹出的组件而不是返回到上一个页面。

    为了解决这个问题,我们可以从路由栈角度思考。一般弹出组件是不会在路由栈上添加任何记录,因此我们在弹出组件时,可以在路由栈中 push 一个记录,为了不让页面跳转,我们可以把跳转的目标路由设置为当前页面路由,并加上一个 query 来标记这个组件弹出的状态。

    然后监听 query 的变化,当点击弹出组件时,query 中与该弹出组件有关的标记变为 true,则将弹出组件设为显示;当用户点击 native 返回键时,路由返回上一个记录,仍然是当前页面路由,不过 query 中与该弹出组件有关的标记不再是 true 了,这样我们就可以把弹出组件设置成隐藏,同时不会返回上一个页面。相关代码如下:

    <template>  <van-cell title="几时入坑"                    is-link                    :value="textData.pitDateStr"                    @click="goToSelect('calendar')" />  <van-popup v-model="showCalendar"              position="right"              :style="{ height: '100%', width: '100%' }">    <Calendar title="选择入坑时间"              @select="onSelectPitDate" />  </van-popup><template/><script lang="ts">...export default class Form extends Vue {  private showCalendar = false;  private goToSelect(popupName: string) {    this.$router.push({ name: 'form', query: { [popupName]: 'true' } });  }  private onSelectPitDate(...res: DateObject[]) {    ...    this.$router.go(-1);  }  @Watch('$route.query')  private handlePopup(val: any) {    switch (true) {      case val.calendar && val.calendar === 'true':        this.showCalendar = true;        break;      default:        this.showCalendar = false;        break;    }  }}</script>
    

    通过 UA 获取设备信息

    在开发 h5 开发时,可能会遇到下面几种情况:

    1. 开发时都是在浏览器进行开发调试的,所以需要避免调用 native 的接口,因为这些接口在浏览器环境根本不存在;

    2. 有些情况需要区分所在环境是在 android webview 还是 ios webview,做一些针对特定平台的处理;

    3. 当 h5 版本已经更新,但是客户端版本并没有同步更新,那么如果之间的接口调用发生了改变,就会出现调用出错。

    所以需要一种方式来检测页面当前所处设备的平台类型、app 版本、系统版本等,目前比较靠谱的方式是通过 android / ios webview 修改 UserAgent,在原有的基础上加上特定后缀,然后在网页就可以通过 UA 获取设备相关信息了。当然这种方式的前提是 native 代码是可以为此做出改动的。以安卓为例关键代码如下:

    安卓关键代码:

    // Activity -> onCreate...// 获取 app 版本PackageManager packageManager = getPackageManager();PackageInfo packInfo = null;try {  // getPackageName()是你当前类的包名,0代表是获取版本信息  packInfo = packageManager.getPackageInfo(getPackageName(),0);} catch (PackageManager.NameNotFoundException e) {  e.printStackTrace();}String appVersion = packInfo.versionName;// 获取系统版本String systemVersion = android.os.Build.VERSION.RELEASE;mWebSettings.setUserAgentString(  mWebSettings.getUserAgentString() + " DSBRIDGE_"  + appVersion + "_" + systemVersion + "_android");
    

    h5 关键代码:

    const initDeviceInfo = () => {  const UA = navigator.userAgent;  const info = UA.match(/s{1}DSBRIDGE[w.]+$/g);  if (info && info.length > 0) {    const infoArray = info[0].split('_');    window.$appVersion = infoArray[1];    window.$systemVersion = infoArray[2];    window.$platform = infoArray[3] as Platform;  } else {    window.$appVersion = undefined;    window.$systemVersion = undefined;    window.$platform = 'browser';  }};
    

    mock 数据

    Mock[71]

    当前后端进度不一致,接口还尚未实现时,为了不影响彼此的进度,此时前后端约定好接口数据格式后,前端就可以使用 mock 数据进行独立开发了。本项目使用了 Mock 实现前端所需的接口。

    调试控制台

    eruda[72]

    vconsole[73]

    在调试方面,本项目使用 eruda 作为手机端调试面板,功能相当于打开 PC 控制台,可以很方便地查看 console, network, cookie, localStorage 等关键调试信息。与之类似地工具还有微信的前端研发团队开发的 vconsole,各位可以选择适合自己项目的工具。

    关于 eruda 使用,推荐使用 cdn 方式加载,至于什么时候加载 eruda,可以根据不同项目制定不同策略。示例代码如下:

    <script>  (function() {    const NO_ERUDA = window.location.protocol === 'https:';    if (NO_ERUDA) return;    const src = 'https://cdn.jsdelivr.net/npm/eruda@1.5.8/eruda.min.js';    document.write('<scr' + 'ipt src="' + src + '"></scr' + 'ipt>');    document.write('<scr' + 'ipt>eruda.init();</scr' + 'ipt>');  })();</script>
    

    抓包工具

    charles[74]

    fiddler[75]

    虽然有了 eruda 调试工具,但某些情况下仍不能满足需求,比如现网完全关闭 eruda 等情况。

    此时就需要抓包工具,相关工具主要就是上面罗列的这两个,各位可以选择适合自己项目的工具。

    通过 charles 可以清晰的查看所有请求的信息(注:https 下抓包需要在手机上配置相关证书)。当然 charles 还有更多强大功能,比例模拟弱网情况,资源映射等。

    推荐一篇不错的 charles 使用教程:

    解锁 Charles 的姿势[76]

    异常监控平台

    sentry[77]

    移动端网页相对 PC 端,主要有设备众多,网络条件各异,调试困难等特点。导致如下问题:

    • 设备兼容或网络异常导致只有部分情况下才出现的 bug,测试无法全面覆盖

    • 无法获取出现 bug 的用户的设备,又不能复现反馈的 bug

    • 部分 bug 只出现几次,后面无法复现,不能还原事故现场

    这时就非常需要一个异常监控平台,将异常实时上传到平台,并及时通知相关人员。

    相关工具有 sentry,fundebug 等,其中 sentry 因为功能强大,支持多平台监控(不仅可以监控前端项目),完全开源,可以私有化部署等特点,而被广泛采纳。

    下面是 sentry 在本项目应用时使用的相关配套工具。

    sentry 针对 javascript 的 sdk

    sentry-javascript[78]

    自动上传 sourcemap 的 webpack 插件

    sentry-webpack-plugin[79]

    编译时自动在 try catch 中添加错误上报函数的 babel 插件

    babel-plugin-try-catch-error-report[80]

    补充:

    前端的异常主要有以下几个部分:

    • 静态资源加载异常

    • 接口异常(包括与后端和 native 的接口)

    • js 报错

    • 网页崩溃

    其中静态资源加载失败,可以通过 window.addEventListener('error', ..., true) 在事件捕获阶段获取,然后筛选出资源加载失败的错误并手动上报错误。核心代码如下:

    // 全局监控资源加载错误window.addEventListener(  'error',  (event) => {    // 过滤 js error    const target = event.target || event.srcElement;    const isElementTarget =      target instanceof HTMLScriptElement ||      target instanceof HTMLLinkElement ||      target instanceof HTMLImageElement;    if (!isElementTarget) {      return false;    }    // 上报资源地址    const url =      (target as HTMLScriptElement | HTMLImageElement).src ||      (target as HTMLLinkElement).href;    this.log({      error: new Error(`ResourceLoadError: ${url}`),      type: 'resource load'    });  },  true);
    

    关于服务端接口异常,可以通过在封装的 http 模块中,全局集成上报错误函数(native 接口的错误上报类似,可在项目中查看)。核心代码如下:

    function errorReport(  url: string,  error: string | Error,  requestOptions: AxiosRequestConfig,  response?: AnyObject) {  if (window.$sentry) {    const errorInfo: RequestErrorInfo = {      error: typeof error === 'string' ? new Error(error) : error,      type: 'request',      requestUrl: url,      requestOptions: JSON.stringify(requestOptions)    };    if (response) {      errorInfo.response = JSON.stringify(response);    }    window.$sentry.log(errorInfo);  }}
    

    关于全局 js 报错,sentry 针对的前端的 sdk 已经通过 window.onerror 和 window.addEventListener('unhandledrejection', ..., false) 进行全局监听并上报。

    需要注意的是其中 window.onerror = (message, source, lineno, colno, error) =>{} 不同于 window.addEventListener('error', ...),window.onerror 捕获的信息更丰富,包括了错误字符串信息、发生错误的 js 文件,错误所在的行数、列数、和 Error 对象(其中还会有调用堆栈信息等)。所以 sentry 会选择 window.onerror 进行 js 全局监控。

    但有一种错误是 window.onerror 监听不到的,那就是 unhandledrejection 错误,这个错误是当 promise reject 后没有 catch 住所引起的。当然 sentry 的 sdk 也已经做了监听。

    针对 vue 项目,也可对 errorHandler 钩子进行全局监听,react 的话可以通过 componentDidCatch 钩子,vue 相关代码如下:

    // 全局监控 Vue errorHandlerVue.config.errorHandler = (error, vm, info) => {  window.$sentry.log({    error,    type: 'vue errorHandler',    vm,    info  });};
    

    但是对于我们业务中,经常会对一些以报错代码使用 try catch,这些错误如果没有在 catch 中向上抛出,是无法通过 window.onerror 捕获的,针对这种情况,笔者开发了一个 babel 插件 babel-plugin-try-catch-error-report[81],该插件可以在 babel[82] 编译 js 的过程中,通过在 ast 中查找 catch 节点,然后再 catch 代码块中自动插入错误上报函数,可以自定义函数名,和上报的内容(源码所在文件,行数,列数,调用栈,以及当前 window 属性,比如当前路由信息 window.location.href)。相关配置代码如下:

    if (!IS_DEV) {  plugins.push([    'try-catch-error-report',    {      expression: 'window.$sentry.log',      needFilename: true,      needLineNo: true,      needColumnNo: false,      needContext: true,      exclude: ['node_modules']    }  ]);}
    

    针对跨域 js 问题,当加载的不同域的 js 文件时,例如通过 cdn 加载打包后的 js。如果 js 报错,window.onerror 只能捕获到 script error,没有任何有效信息能帮助我们定位问题。此时就需要我们做一些事情:第一步、服务端需要在返回 js 的返回头设置 Access-Control-Allow-Origin: *第二部、设置 script 标签属性 crossorigin,代码如下:

    <script src="http://helloworld/main.js" crossorigin></script>
    

    如果是动态添加的,也可动态设置:

    const script = document.createElement('script');script.crossOrigin = 'anonymous';script.src = url;document.body.appendChild(script);
    

    针对网页崩溃问题,推荐一个基于 service work 的监控方案,相关文章已列在下面的。如果是 webview 加载网页,也可以通过 webview 加载失败的钩子监控网页崩溃等。

    如何监控网页崩溃?[83]

    最后,因为部署到线上的代码一般都是经过压缩混淆的,如果没有上传 sourcemap 的话,是无法定位到具体源码的,可以现在 项目中添加 .sentryclirc 文件,其中内容可参考本项目的 .sentryclirc,然后通过 sentry-cli (需要全局全装 sentry-cli 即npm install sentry-cli)命令行工具进行上传,命令如下:

    sentry-cli releases -o 机构名 -p 项目名 files 版本 upload-sourcemaps sourcemap 文件相对位置 --url-prefix js 在线上相对根目录的位置 --rewrite// 示例sentry-cli releases -o mcukingdom -p hello-world files 0.2.1 upload-sourcemaps dist/js --url-prefix '~/js/' --rewrite
    

    当然官方也提供了 webpack 插件 sentry-webpack-plugin[84],当打包时触发 webpack 的 after-emit 事件钩子(即生成资源到 output 目录之后),插件会自动上传打包目录中的 sourcemap 和关联的 js,相关配置可参考本项目的 vue.config.js 文件。

    通常为了安全,是不允许在线上部署 sourcemap 文件的,所以上传 sourcemap 到 sentry 后,可手动删除线上 sourcemap 文件。

    常见问题

    • iOS WKWebView cookie 写入慢以及易丢失

      现象:

      原因:WKWebView 对 NSHTTPCookieStorage 写入 cookie,不是实时存储的。从实际的测试中发现,不同的 IOS 版本,延迟的时间还不一样。同样,发起请求时,也不是实时读取,无法做到和 native 同步,导致页面逻辑出错。

      两种解决办法:

      各位可以选择适合自己项目的方式,有更好的处理方式欢迎留言。

    1. 客户端手动干预一下 cookie 的存储。将服务响应的 cookie,持久化到本地,在下次 webview 启动时,读取本地的 cookie 值,手动再去通过 native 往 webview 写入。但是偶尔还有 spa 的页面路由切换的时候丢失 cookie 的问题。

    2. 将 cookie 存储的 session 持久化到 localSorage,每次请求时都会取 localSorage 存储的 session,并在请求头部添加 cookieback 字段,服务端鉴权时,优先校验 cookieback 字段。这样即使 cookie 丢失或存储的上一次的 session,都不会有影响。不过这种方式相当于绕开了 cookie 传输机制,无法享受 这种机制带来的安全特性。

    1. iOS 登陆后立即进入网页,会出现 cookie 获取不到或获取的上一次登陆缓存的 cookie

    2. 重启 App 后,cookie 会丢失

  • input 标签在部分安卓 webview 上无法实现上传图片功能

    因为 Android 的版本碎片问题,很多版本的 WebView 都对唤起函数有不同的支持。我们需要重写 WebChromeClient 下的 openFileChooser()(5.0 及以上系统回调 onShowFileChooser())。我们通过 Intent 在 openFileChooser()中唤起系统相机和支持 Intent 的相关 app。

    相关文章:【Android】WebView 的 input 上传照片的兼容问题[85]

  • input 标签在 iOS 上唤起软键盘,键盘收回后页面不回落(部分情况页面看上去已经回落,实际结构并未回落)

    input 焦点失焦后,ios 软键盘收起,但没有触发 window resize,导致实际页面 dom 仍然被键盘顶上去--错位。解决办法:全局监听 input 失焦事件,当触发事件后,将 body 的 scrollTop 设置为 0。

    document.addEventListener('focusout', () => {
      document.body.scrollTop = 0;
    });
    
  • 唤起软键盘后会遮挡输入框

    当 input 或 textarea 获取焦点后,软键盘会遮挡输入框。解决办法:全局监听 window 的 resize 事件,当触发事件后,获取当前 active 的元素并检验是否为 input 或 textarea 元素,如果是则调用元素的 scrollIntoViewIfNeeded 即可。

    window.addEventListener('resize', () => {
      // 判断当前 active 的元素是否为 input 或 textarea
      if (
        document.activeElement!.tagName === 'INPUT' ||
        document.activeElement!.tagName === 'TEXTAREA'
      ) {
        setTimeout(() => {
          // 原生方法,滚动至需要显示的位置
          document.activeElement!.scrollIntoView();
        }, 0);
      }
    });
    
  • 唤起键盘后 position: fixed;bottom: 0px; 元素被键盘顶起

    解决办法:全局监听 window 的 resize 事件,当触发事件后,获取 id 名为 fixed-bottom 的元素(可提前约定好如何区分定位在窗口底部的元素),将其设置成 display: none。键盘收回时,则设置成 display: block;

    const clientHeight = document.documentElement.clientHeight;
    window.addEventListener('resize', () => {
      const bodyHeight = document.documentElement.clientHeight;
      const ele = document.getElementById('fixed-bottom');
      if (!ele) return;
      if (clientHeight > bodyHeight) {
        (ele as HTMLElement).style.display = 'none';
      } else {
        (ele as HTMLElement).style.display = 'block';
      }
    });
    
  • 点击网页输入框会导致网页放大通过 viewport 设置 user-scalable=no 即可,(注意:当 user-scalable=no 时,无需设置 minimum-scale=1, maximum-scale=1,因为已经禁止了用户缩放页面了,允许的缩放范围也就不存在了)。代码如下:

    <meta
      name="viewport"
      content="width=device-width,initial-scale=1.0,user-scalable=0,viewport-fit=cover"
    />
    
  • webview 通过 loadUrl 加载的页面运行时却通过第三方浏览器打开,代码如下

    // 创建一个 Webview
    Webview webview = (Webview) findViewById(R.id.webView);
    // 调用 Webview loadUrl
    webview.loadUrl("http://www.baidu.com/");
    

    解决办法:在调用 loadUrl 之前,设置下 WebviewClient 类,当然如果需要也可自己实现 WebviewClient(例如通过拦截 prompt 实现 js 与 native 的通信)

    webview.setWebViewClient(new WebViewClient());
    
  • 参考资料

    [1]

    mattermost-mobile: https://github.com/mattermost/mattermost-mobile

    [2]

    mobile-web-best-practice: https://github.com/mcuking/mobile-web-best-practice

    [3]

    vue-cli3: https://cli.vuejs.org/

    [4]

    typescript: http://www.typescriptlang.org/

    [5]

    react: https://reactjs.org/

    [6]

    组件库: #组件库

    [7]

    JSBridge: #jsbridge

    [8]

    路由堆栈管理(模拟原生 APP 导航): #路由堆栈管理模拟原生-app-导航

    [9]

    请求数据缓存: #请求数据缓存

    [10]

    构建时预渲染: #构建时预渲染

    [11]

    Webpack 策略: #webpack-策略

    [12]

    基础库抽离: #基础库抽离

    [13]

    手势库: #手势库

    [14]

    样式适配: #样式适配

    [15]

    表单校验: #表单校验

    [16]

    阻止原生返回事件: #阻止原生返回事件

    [17]

    通过 UA 获取设备信息: #通过-ua-获取设备信息

    [18]

    mock 数据: #mock-数据

    [19]

    调试控制台: #调试控制台

    [20]

    抓包工具: #抓包工具

    [21]

    异常监控平台: #异常监控平台

    [22]

    常见问题: #常见问题

    [23]

    vant: https://youzan.github.io/vant/#/zh-CN/intro

    [24]

    vux: https://github.com/airyland/vux

    [25]

    mint-ui: https://github.com/ElemeFE/mint-ui

    [26]

    cube-ui: https://github.com/didi/cube-ui

    [27]

    less-loader: https://github.com/webpack-contrib/less-loader

    [28]

    less: http://lesscss.org/

    [29]

    modifyVars: http://lesscss.org/usage/#using-less-in-the-browser-modify-variables

    [30]

    定制主题: https://youzan.github.io/vant/#/zh-CN/theme

    [31]

    Vue 常用组件库的比较分析(移动端): https://blog.csdn.net/weixin_38633659/article/details/89736656

    [32]

    DSBridge-IOS: https://github.com/wendux/DSBridge-IOS

    [33]

    DSBridge-Android: https://github.com/wendux/DSBridge-Android

    [34]

    WebViewJavascriptBridge: https://github.com/marcuswestin/WebViewJavascriptBridge

    [35]

    mobile-web-best-practice-container: https://github.com/mcuking/mobile-web-best-practice-container

    [36]

    JSBridge: https://github.com/mcuking/JSBridge

    [37]

    JSBridge 实现原理: https://github.com/mcuking/JSBridge

    [38]

    vue-page-stack: https://github.com/hezhongfeng/vue-page-stack

    [39]

    vue-navigation: https://github.com/zack24q/vue-navigation

    [40]

    vue-stack-router: https://github.com/luojilab/vue-stack-router

    [41]

    vue-router: https://router.vuejs.org/

    [42]

    【vue-page-stack】Vue 单页应用导航管理器 正式发布: https://juejin.im/post/5d2ef417f265da1b971aa94f

    [43]

    Vue 社区的路由解决方案:vue-stack-router: https://juejin.im/post/5d4ce4fd6fb9a06acd450e8c

    [44]

    mem: https://github.com/sindresorhus/mem

    [45]

    nuxt.js: https://github.com/nuxt/nuxt.js

    [46]

    next: https://github.com/zeit/next.js

    [47]

    Puppeteer: https://github.com/GoogleChrome/puppeteer

    [48]

    Phantomjs: https://github.com/ariya/phantomjs

    [49]

    prerender-spa-plugin: https://github.com/chrisvfritz/prerender-spa-plugin

    [50]

    vue 预渲染之 prerender-spa-plugin 解析(一): https://blog.csdn.net/vv_bug/article/details/84593052

    [51]

    使用预渲提升 SPA 应用体验: https://juejin.im/post/5d5fa22ee51d4561de20b5f5

    [52]

    webpack-dll-plugin: https://webpack.docschina.org/plugins/dll-plugin/

    [53]

    Externals: https://webpack.docschina.org/configuration/externals/

    [54]

    Webpack 优化——将你的构建效率提速翻倍: https://juejin.im/post/5d614dc96fb9a06ae3726b3e

    [55]

    hammer.js: https://github.com/hammerjs/hammer.js

    [56]

    AlloyFinger: https://github.com/AlloyTeam/AlloyFinger

    [57]

    H5 案例分享:JS 手势框架 —— Hammer.js: https://www.h5anli.com/articles/201609/hammerjs.html

    [58]

    使用 require.context 实现前端工程自动化: https://www.jianshu.com/p/c894ea00dfec

    [59]

    postcss-px-to-viewport: https://github.com/evrone/postcss-px-to-viewport

    [60]

    Viewport Units Buggyfill: https://github.com/rodneyrehm/viewport-units-buggyfill

    [61]

    flexible: https://github.com/amfe/lib-flexible

    [62]

    postcss-pxtorem: https://github.com/cuth/postcss-pxtorem

    [63]

    Autoprefixer: https://github.com/postcss/autoprefixer

    [64]

    browserslist: https://github.com/browserslist/browserslist

    [65]

    Viewport Units Buggyfill: https://github.com/rodneyrehm/viewport-units-buggyfill

    [66]

    rem-vw-layout: https://github.com/imwtr/rem-vw-layout

    [67]

    细说移动端 经典的 REM 布局 与 新秀 VW 布局: https://www.cnblogs.com/imwtr/p/9648233.html

    [68]

    如何在 Vue 项目中使用 vw 实现移动端适配: https://www.jianshu.com/p/1f1b23f8348f

    [69]

    async-validator: https://github.com/yiminghe/async-validator

    [70]

    vee-validate: https://github.com/baianat/vee-validate

    [71]

    Mock: https://github.com/nuysoft/Mock

    [72]

    eruda: https://github.com/liriliri/eruda

    [73]

    vconsole: https://github.com/Tencent/vConsole

    [74]

    charles: https://www.charlesproxy.com/

    [75]

    fiddler: https://www.telerik.com/fiddler

    [76]

    解锁 Charles 的姿势: https://juejin.im/post/5a1033d2f265da431f4aa81f

    [77]

    sentry: https://github.com/getsentry/sentry

    [78]

    sentry-javascript: https://github.com/getsentry/sentry-javascript

    [79]

    sentry-webpack-plugin: https://github.com/getsentry/sentry-webpack-plugin

    [80]

    babel-plugin-try-catch-error-report: https://github.com/mcuking/babel-plugin-try-catch-error-report

    [81]

    babel-plugin-try-catch-error-report: https://github.com/mcuking/babel-plugin-try-catch-error-report

    [82]

    babel: https://babeljs.io/

    [83]

    如何监控网页崩溃?: https://juejin.im/entry/5be158116fb9a049c6434f4a

    [84]

    sentry-webpack-plugin: https://github.com/getsentry/sentry-webpack-plugin

    [85]

    【Android】WebView 的 input 上传照片的兼容问题: https://juejin.im/post/5a322cdef265da43176a2913

    END开发者技术前线 ,汇集技术前线快讯和关注行业趋势,大厂干货,是开发者经历和成长的优秀指南。历史推荐花了三个月终于把所有的 Python 库全部整理了!可以说很全面了Python 学习路线思维导图, 经典收藏!支付宝海量支付背后的架构揭秘!好文点个在看吧!
    
    展开全文
  • 作者:mcuking(杭州个推)https://juejin.im/post/5d759f706fb9a06afa32adec笔者在公司用 web 技术开发移动端应用已经...
  • 作者:mucking| 来源:掘金https://juejin.im/post/5d759f706fb9a06afa32adec笔者在公司用 web 技术开发移动端应用已经有一年多的时间...
  • 作者:mucking| 来源:掘金https://juejin.im/post/5d759f706fb9a06afa32adec笔者在公司用 web 技术开发移动端应用已经有一年多的时间...
  • 笔者最近在公司用 web 技术开发移动端应用已经有一年多的时间了,开始主要以 vue 技术栈配合 native 为主,目前演进成 vue + react native 技术架构,vue 主要负责开发 OA 业务,react native 主要负责即时通信部分...
  • 移动 Web 最佳实践

    2020-01-03 05:22:53
    本文为作者mcuking投稿,原文链接:https://juejin.im/post/5d759f706fb9a06afa32adec笔者在公司用 web 技术开发移动端应用已经有一年多...
  • 第一阶段课程大纲 第 01 天 HTML初体会 教学目标:了解什么是页面. 掌握html骨架的书写以及骨架中标签的作用. 快捷键操作. 相关字符编码. html标签和属性的书写语法 HTTP 协议 ...教学目标:了解废弃标签....
1
收藏数 16
精华内容 6
关键字:

webpack 引入dsbridge