xixigiggling 发布的文章

经历了一波感觉暗无天日的秋招以后,总算能稍微松口气了,先把自己一直想写的博客,记录一下

源码的入口

首先,可以看到的是koa源码中就只有lib目录里面有四个文件:

  • application.js
  • context.js
  • request.js
  • response.js

按我们的使用方法:

const Koa = require('koa');
const app = new Koa();

首先第一个疑问就来了:由上面的代码可知require('koa')得到了koa的构造函数,那么,koa的构造函数是通过哪个文件暴露出来的呢?

换言之,也就是我们阅读koa源码从哪里开始呢?

于是得知道require('koa')发生了什么,已知require是node的模块机制

require()方法接受一个标识符作为参数。在Node实现中,正是基于这样一个标识符进行模块查找的。模块标识符在Node中主要分为以下几类:

  • 核心模块,如http,fs等
  • .或..开始的相对路径文件模块
  • 以/开始的绝对路径文件模块
  • 非路径形式的文件模块,如自定义的connect模块

于是我们引入的便属于自定义模块:

自定义模块的查找:

  1. 当前文件目录下的node_modules目录
  2. 父目录下的node_modules目录
  3. 父目录的父目录下的node_modules目录
  4. 沿着路径向上逐级递归,直到根目录的node_modules目录

于是进一步揭晓:先到我们的当前文件目录下的node_modules目录查找,找到了查找的地方,然后呢?

但在文件的定位过程中,还有一些细节需要注意,这主要包括文件拓展名的分析、目录和包的处理

于是首先,koa这个标识符不包含文件拓展名,Node会按.js,.json,.node的次序补足拓展名依次尝试,但是我们可以看自己的node_modules目录里面koa是一个目录...

此时Node会将目录当作一个包来处理

那么是一个包就会有package.json,于是:

Node会在当前目录下查找package.json,通过JSON.parse()解析出包描述对象,从而取出main属性指定的文件名进行定位
如果文件名缺少扩展名,则进入扩展名分析环节
如果文件名错误,或者没有package.json,则Node会把index当做默认文件名,然后依次查找index.js,index.json,index.node
...

注意这里如果package.json没有main属性,应该也是直接使用index当做默认文件名,例如koa-compose的包:

  • History.md
  • index.js(直接在index中module.exports = compose了)
  • package.json
  • Readme.md(没有main属性)

回到koa源码中,正好node_modules的koa目录下的package.json中有main属性:

"main": "lib/application.js"

然后再去application.js中去查找:module.exports = class Application extends Emitter { // code }

答案揭晓:通过require('koa')得到的Koa构造函数,其实是lib/application.js中暴露出来的class Application

于是,终于走了小小的一步...

参考:《深入浅出Node.js》

开始探索

我们知道了const Koa = require('koa');,中的Koa代表的是,class Application,接下来回到我们要探索的问题:为什么koa会表现成一个洋葱模型?

如下:

const Koa = require("koa");
const app = new Koa();

app.use(async (ctx, next) => {
    console.log(1);
    await next();
    console.log(2);
});

app.use(async (ctx, next) => {
    console.log(3);
    await next();
    console.log(4);
});

app.use(ctx => {
    ctx.body = "Hello Koa";
});

app.listen(3000);
console.log('server start: 3000');

// output:
// 1
// 3
// 4
// 2

所以我们开始从application.js中寻找答案,首先示例代码中使用了use,我们可以从application.js中去找到use方法:

 use(fn) {
    // 边界判断
    if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
    // 如注释所示,为了转化generator
    if (isGeneratorFunction(fn)) {
      deprecate('Support for generators will be removed in v3. ' +
                'See the documentation for examples of how to convert old middleware ' +
                'https://github.com/koajs/koa/blob/master/docs/migration.md');
      fn = convert(fn);
    }
    debug('use %s', fn._name || fn.name || '-');
    // 这里是核心
    this.middleware.push(fn);
    return this;
  }

由于构造函数中,this.middleware = [];,得知应该是把fn放入数组中存储起来

于是这里use函数就结束了,然后就到了app.listen(3000),接着看listen方法

listen(...args) {
    debug('listen');
    // 使用了http模块的createServer与Appalication的callback方法
    const server = http.createServer(this.callback());
    return server.listen(...args);
}

然后我们再看callback方法:

callback() {
    // 把之前存储函数middleware,传给了compose函数?
    const fn = compose(this.middleware);

    if (!this.listenerCount('error')) this.on('error', this.onerror);

    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };

    return handleRequest;
  }
// todo

需求:有一个嵌入iframe的网页,在右半边显示,但是右侧是自适应宽高的,直接设置iframe: width:100%; height:100%可以实现这个自适应父元素的功能,但是却出现了,原来的网页太大只能显示局部,那么能不能实现自适应父元素的宽高的前提下,仍能缩小一下页面的比例呢?

- 阅读剩余部分 -

fiddle 抓包:

主要参考:微信内移动前端开发抓包调试工具fiddler使用教程

还有一篇译文:使用Fiddler对IOS系统进行HTTP抓包

Fiddler实现手机抓包——小白入门

fiddle过滤指定链接:只显示我们需要查看的链接情况:

右侧的Filters --> 勾选 use Filters --> 选第二个选项为:show only following hosts ,然后填写你需要监控的hosts,可以填写多个,按分号隔开即可

使用fiddle时发现chrome不断的向google发现数据...

打开chrome,就反复不断的请求clients1.google.com

有说:

在设置中,选高级,可以看到自动填充。
取消“密码和表单”中的“自动填充”功能,即可解决clients1.google.com请求的问题。
这个是谷歌的个人信息同步的请求

楼主的解决办法是:

我的解决办法是设置Filter,在请求上按右键,hide client1.google.com
或者干脆开启全局的Filters,设置 Hide if URL contains 为 client1.google.com

以前就遇到这样的问题,但利用一些基本的百度,谷歌也能解决了问题,但是这次又遇到了,又出问题了...,记录一下

vue部署到Tomcat

后端同学用的tomcat,直接懒的麻烦,就打包给放到tomcatwebapps目录下

- webapps
  - dist
     - index.html
     - static

这样直接就xxx/dist来进行访问.......

部署过程,参考:为什么vue+webpack需要用到node,如何部署项目到服务器?

总结一下:以刚刚我部署的为例

单页面应用最重要的文件app.js,在wepack打包后在index.html中的引用是这样的:

<script type="text/javascript" src="/static/js/app.js"></script>

但我们放到服务器上后,访问xxx/dist时,实际上app.js的路径是xxx/static/js/app.js,也就是说此时index.html应该要变成如下才对:

<!-- 放到服务器后 -->
<script type="text/javascript" src="/dist/static/js/app.js"></script>

但我们不可能每次都手动去改吧,于是只要在webpackconfig/index.jsbuild下修改成:

assetsPublicPath: './'
或者
assetsPublicPath: '/dist/'  (你放tomcat webapps下的目录名)

这样就成功一半了,但是记得我们还使用了vue-router啊,部署到线上会采用history模式【源码拾遗】从vue-router看前端路由的两种实现),此时路由也要变成相对于/dist/(你放tomcat webapps下的目录名)来发请求啊

mode: 'history',
base: 'dist',

于是就完美了!

但有一个问题:history模式,当用户直接输入url(输错),例如xxx/dist/ddd,这时是找不到路由的,在Tomcat里面404页面该怎么跳回首页呢?

新建一个WEB-INF目录,在WEB-INF目录下新建一个web.xml

- static
- index.html
- WEB-INF
  - web.xml

web.xml中如下,配置一下当404时,跳转回首页

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0"
  metadata-complete="true">

  <display-name>webapp</display-name>
  <description>
     webapp
  </description>
  <error-page>  
   <error-code>404</error-code>  
   <location>/index.html</location>
</error-page>  
</web-app>

此时注意还需在router中配置一下,当404时,匹配到的路由:

{
    path: '*',
    name: 'errorPage',
    component: require('xxx').default  // 你的首页组件
},

参考资源:
Vue-router History模式下,空白页面,如何配置tomcat服务器
vue路由中的mode : 'history',
HTML5 History 模式,个人觉得官方文档解释的并没有太清楚.....

vue部署到Nginx

待续...

vue部署到node

待续...

起因遇到一个使用了vantUI框架,但需要定制dialog的需求,所以只能去看vant的transition动画的实现,来满足自己的需求...

查看vant的源码

vant的源码可以在这里看到:https://github.com/youzan/vant/blob/dev/packages/vant-css/src/dialog.css

其中dialog的动效在这:

&-bounce-enter {
    opacity: 0;
    transform: translate3d(-50%, -50%, 0) scale(0.7);
}

&-bounce-leave-active {
    opacity: 0;
    transform: translate3d(-50%, -50%, 0) scale(0.9);
}

这个是用来控制dialog的中间部分的内容弹出的时候的变形动画(..不太好表述),但是试一下就会发现问题,这里没有mask啊,也就是说这里弹出dialog底部的淡黑色的遮罩的动效还得另找...

在公共的css样式中找到了一个fade-in/outhttps://github.com/youzan/vant/blob/dev/packages/vant-css/src/common/animation.css

.van-fade {
  &-enter-active {
    animation: .3s van-fade-in;
  }

  &-leave-active {
    animation: .3s van-fade-out;
  }
}

@keyframes van-fade-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes van-fade-out {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

再平时使用vant的dialog组件时能查看到:

<!-- 未弹出前 -->
<div class="van-dialog" style="z-index: 2003; display: none;">...</div>
<div class="van-modal" style="z-index: 2003; display: none;"></div>

<!-- 弹出后(同时有class的切换) -->
<div class="van-dialog" style="z-index: 2005;">...</div>
<div class="van-modal" style="z-index: 2004;"></div>

可以猜测到,dialog和modal(也就是那个底部的mask)是一种同级组件的关系,而且应该是两个动画,因为同时有class的切换,最后的z-index也说明了这点,因为弹出后是同级关系,所以需要用z-index来控制内容显示在mask之上

使用chrome-devTools的animations工具

以上也只是猜测,想到动画,作为万能的chrome-devTools肯定会有检查动画的工具吧,果然:

google官方-检查动画(需翻墙)

Chrome DevTools谷歌浏览器开发者工具如何检查动画(不好翻墙的可以参考这个)

在devTools的面板里面打开右边三个竖点的菜单,打开more Tools选项,其中的animations就是动画检查工具了

两个用途:

  • 检查动画: 慢速播放、重播或检查动画组的源代码; 动画检查器支持 CSS 动画、CSS 过渡和网络动画,当前不支持 requestAnimationFrame 动画
  • 修改动画: 修改动画组的时间、延迟、持续时间或关键帧偏移,不支持编辑贝塞尔曲线和关键帧

使用:

打开后,直接在页面开始的动画就会被记录下来,而且会被记录为一个个的动画组

动画组的概念:

例如这里是vant的dialog动画被记录下来的两个动画组,我们点击第一个就可以发现,第一个动画组是mask遮罩层的弹出动画,第二个动画组是dialog的弹出动画,而底部还有详细的动画元素,动画过程

控制动画播放速度:

在上述图示的1部分100%, 25%, 10%,就可以进行动画的速度进行更改

在底部的动画帧图中,可以对动画进行

延后动画/改变动画:

在上述图示的4部分,我们可以对动画过程进行控制,通过整体拖动那个扇形区域就可以进行动画的延时,例如这样就可以把mask动画和dialog动画区分的更清晰,通过缩短那个扇形区域可以达到改变动画效果的作用

最终效果

有了工具的帮助,我们可以很准确的判断出有两个动画,而且可以对动画过程进行延时,减速,让我们对整个动画过程有精准的控制

于是我们用两个组件来做,一个mask组件(用来fade-in/out的效果),一个dialog组件(做transform效果)

最终代码如下:

<!-- dialog组件 -->
<template>
    <!--<div>-->
    <transition name="move">
        <div class="dialog" >
            <div class="content">
                <button @click="dispear">关闭</button>
            </div>
        </div>
    </transition>
</template>

<script type="text/ecmascript-6">
    export default {
        data() {
            return {
            }
        },
        methods: {
            dispear() {
                this.$emit('close');
            }
        },
        components: {}
    }
</script>

<style lang="scss" scoped>
    .dialog {
        position: fixed;
        top: 50%;
        left: 50%;
        width: 85%;
        font-size: 16px;
        overflow: hidden;
        transition: .3s;
        border-radius: 4px;
        background-color: #fff;
        transform: translate3d(-50%, -50%, 0);
        z-index: 90;
        .content {
            width: 300px;
            height:200px;
            margin: 0 auto;
        }
        &.move-enter {
            opacity: 0;
            transform: translate3d(-50%, -50%, 0) scale(0.7);
        }
        &.move-leave-active {
            opacity: 0;
            transform: translate3d(-50%, -50%, 0) scale(0.9);
        }
    }
</style>
<!-- mask组件 -->
<template>
    <transition name="van-fade">
        <div class="mask"></div>
    </transition>
</template>

<script type="text/ecmascript-6">
    export default {
        data() {
            return {}
        },
        methods: {},
        components: {}
    }
</script>

<style lang="scss" scoped>
    .mask {
        position: fixed;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        opacity: 1;
        background-color: rgba(0, 0, 0, 0.7);
    }
    van-fade {
        &-enter-active {
            animation: .3s van-fade-in;
        }

        &-leave-active {
            animation: .3s van-fade-out;
        }
    }
    @keyframes van-fade-in {
        from {
            opacity: 0;
        }

        to {
            opacity: 1;
        }
    }
    @keyframes van-fade-out {
        from {
            opacity: 1;
        }

        to {
            opacity: 0;
        }
    }
</style>

使用的时候,两个组件同时引用即可,最终效果:

总结:其实一开始没想到是有两个动效的,所以一直也没做出来,之后就放弃了,由于的确有一些弹窗没办法,只能去定制,所以就再次硬着头皮上,没想到原来是有两个transition,加上同级表示来做的(再次说明要善用chrome Devtools),感悟一句:chrome Devtools其实真的是前端入门的地方

border-raduis

有两种写法:

第一种;

/*单值写法(简写),会被解释为 top-left | top-right | bottom-right | bottom-left*/
border-raduis: 20px
/*类似于margin的简写化,顺时针*/
border-raduis: 20% 10%;

第二种:

/*加斜杠的写法,八个值(斜杠前4个值,后4个值)*/
border-raduis: 20% / 10%;

重点就在第二种写法:

它表示的是椭圆,斜杠前是椭圆的长半轴,斜杠后是椭圆的短半轴

例如:

border-raduis: 50% 50% 50% 50% / 10% 50% 50% 50%;

这里表示的就是,左上角的圆是斜杠前第一个50%(充当长半轴),和斜杠后第一个50%(充当短半轴),四个角的每个圆角都是这样一一配对而成的

效果如图:

demo

border-radius MDN

开篇比较大,后续有新感知再继续完善

一直使用的是console.log()来打印输出的形式,来debug,如果利用一下chrome的devtool工具来帮助我们debug会带来很大的遍历,如果有后端基础,例如javaweb中常用的log4j,强大的junit,这些辅助工具可以在项目的运行中给予我们很多的信息,可以快速的定位bug

- 阅读剩余部分 -