落霞与孤鹜齐飞

落霞与孤鹜齐飞 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

落霞与孤鹜齐飞 发布了文章 · 2019-06-15

VueRouter

当 <router-link> 对应的路由匹配成功,将自动设置 class 属性值 .router-link-active 。

默认 hash 模式:使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
history 模式:充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面,此模式如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面。

动态路由匹配
{path: '/user/:id', component: User}
user/abc user/123 都将映射到相同的路由
this.$route.params.id(template 中 $route.params.id)

{path: '/user-*'} 匹配以 `/user-` 开头的任意路径

嵌套路由
User 需要 <router-view></router-view>
children 路径不能以 "/" 开头
{
  path: '/user',
  component: User,
  children: [
    {
      // 当 /user/profile 匹配成功,
      // UserProfile 会被渲染在 User 的 <router-view> 中
      path: 'profile',
      component: UserProfile
    },
    {
      // 当 /user/posts 匹配成功
      // UserPosts 会被渲染在 User 的 <router-view> 中
      path: 'posts',
      component: UserPosts
    }
  ]
}

编程式的导航
<router-link :to="..."> 等同于调用 router.push(...)
this.$router.push(location, onComplete?, onAbort?)
this.$router.push('home')
this.$router.push({ path: 'register', query: { plan: 'private' }})
this.$router.push({ name: 'user', params: { userId: '123' }})

location 如果提供了 path,params 会被忽略,解决办法:{path: `register/${id}`}

onComplete 和 onAbort 两个回调用于导航成功完成(在所有的异步钩子被解析之后)或终止(导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由)的时候进行相应的调用

<router-link :to="..." replace> 等同于调用 router.replace(...),和 router.push() 唯一的不同就是,它不会向 history 添加新记录,而是替换掉当前的 history 记录

router.go(n) 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)。


命名视图
<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>

{
  path: '/',
  components: {
    default: Foo,
    a: Bar,
    b: Baz
  }
}


重定向和别名
{ path: '/a', redirect: '/b' }
{ path: '/a', redirect: { name: 'foo' }}
{ path: '/a', redirect: to => {
  // 方法接收 目标路由 作为参数
  // return 重定向的 字符串路径/路径对象
}}

{ path: '/a', component: A, alias: '/b' }


导航守卫
全局前置守卫
router.beforeEach((to, from, next) => {
  // to: Route: 即将要进入的目标 路由对象
  // from: Route: 当前导航正要离开的路由
  // next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
  // some code
  next()
})

全局后置钩子
router.afterEach((to, from) => {
  // ...
})

路由独享的守卫
{
  path: '/foo',
  component: Foo,
  beforeEnter: (to, from, next) => {/* */}
}

组件内的守卫
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 【不能】获取组件实例 `this`,不过,你可以通过传一个回调给 next来访问组件实例
    next(vm => {
      // 通过 `vm` 访问组件实例
    })
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

路由元信息
{
  path: 'bar',
  component: Bar,
  meta: { requiresAuth: true, title: 'BAR' }
}
遍历 $route.matched 来检查路由记录中的 meta 字段。

滚动行为
scrollBehavior 只在支持 history.pushState 的浏览器中可用。
new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。
    // return 期望滚动到哪个的位置
  }
})


路由懒加载
const Foo = () => import('./Foo.vue')

https://router.vuejs.org/zh/

查看原文

赞 5 收藏 3 评论 0

落霞与孤鹜齐飞 发布了文章 · 2019-06-15

Vue 组件通信详解

父子组件通信: props、 $parent / $children、 provide / inject 、 ref 、  $attrs / $listeners
兄弟组件通信:EventBus 、 Vuex
跨级组件通信: EventBus 、 Vuex 、 provide / inject 、 $attrs / $listeners

父传子 子组件用 props 接收,父组件用 v-bind:prop 发送
父组件
<template>
  <div class="section">
    <com-article :articles="articleList"></com-article>
  </div>
</template>
<script>
import comArticle from "./comArticle";
export default {
  data() {
    return {
      articleList: ["红楼梦", "西游记", "三国演义", "水浒传"]
    }
  },
  components: { comArticle },
}
</script>
子组件
<template>
  <ul>
    <li v-for="(item, index) in articles" :key="index">{{item}}</li>
  </ul>
</template>
<script>
export default {
  props: ["articles"]
}
</script>


子传父 子组件用 v-on:click="" this.$emit('name', this.msg)(【有的版本名称只能小写】)发送,父组件自定义事件 v-on:name="getChildValue" 然后在 getChildValue(data){} 方法中接收
父组件
<template>
  <div class="section">
    <com-article @onEmitIndex="onEmitIndex"></com-article> 【不能加括号】
    <ul>
      <li v-for="(item, index) in articles" :key="index">{{item}}</li>
    </ul>
  </div>
</template>
<script>
import comArticle from "./com2";
export default {
  data() {
    return {
        articles:[]
    };
  },
  components: { comArticle },
  methods: {
    onEmitIndex(data) {
      this.articles = data;
    }
  }
}
</script>
子组件
<template>
  <div>
    <button @click="emitIndex()">点击把articleList传给父组件</button> 【可以传参】
  </div>
</template>

<script>
export default {
  data() {
    return {
      articleList: ["红楼梦", "西游记", "三国演义", "水浒传"]
    };
  },
  methods: {
    emitIndex() {
      this.$emit("onEmitIndex", this.articleList); // 
    }
  }
}
</script>


父子传参还可以用 $parent(对象)和 $children(数组)


provide / reject (上传下)
父辈组件中通过 provide 来提供变量,子孙组件中通过 reject 来注入变量。
父组件
<template>
  <div>
    com1 是父组件
    <com2></com2>
  </div>
</template>
<script>
  import com2 from './com2.vue'
  export default {
    provide: {
      msg: "这是父辈组件 com1 传出去的数据"
    },
    components:{
      com2
    }
  }
</script>
子组件
<template>
  <div>
    com2 是 com1 的子组件
    {{demo}}
    <com3></com3>
  </div>
</template>
<script>
  import com3 from './com3.vue'
  export default {
    inject: ['msg'],
    data() {
      return {
        demo: this.msg
      }
    },
    components: {
      com3
    }
  }
</script>
孙组件
<template>
  <div>
    com3 是 com1 的孙组件
    {{msg}}
  </div>
</template>
<script>
  export default {
    inject: ['msg']
  }
</script>


ref
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据 ref="xx" this.$refs.xx

eventBus(事件总线,项目较大难以维护,组件都可以传) $emit(name, data)发送 $on(name, data=>{})接收 【名称小写】
event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()

com1.vue 发送事件
<button @click="additionHandle">加法器</button>
import {EventBus} from './event-bus.js'
data(){
  return {num: 1}
},
additionHandle(){
  EventBus.$emit('addition', {num: this.num++}
)

com2.vue 接收事件
<div>计算和: {{count}}</div>
data() {
  return {count: 0}
},
mounted() {
  EventBus.$on('addition', param => {
    this.count = this.count + param.num;
  })
}


localStorage / sessionStorage
因为 window.loacalStorage.setItem(key, value)、window.loacalStorage.getItem(key) 储存的是字符串,需要用 JSON.parse() / stringify() 转换
可结合 vuex,实现数据持久保存和解决数据及状态混乱问题


$attrs $listeners(仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用)
test.vue
<template>
  <div>
    test.vue
    <child-com1 :name="name" :age="age" :gender="gender" :height="height" title="test.vue 传出的值"></child-com1>
  </div>
</template>
<script>
const childCom1 = () => import("./com1.vue");
export default {
  components: { childCom1 },
  data() {
    return {
      name: "zhangsan",
      age: "18",
      gender: "女",
      height: "158"
    };
  }
};
</script>
<style scoped>
div{
    background-color: #ddd;
}
</style>
com1.vue
<template>
  <div class="com1">
    com1
    <p>name: {{name}}</p>
    <p>childCom1的$attrs: {{$attrs}}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = () => import("./com2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false, // 关闭自动挂载到组件根元素上的没有在 props 声明的属性
  props: {
    name: String
  },
  created() {
    console.log(this.$attrs);
    // {age: "18", gender: "女", height: "158", title: "test.vue 传 com1.vue"}
  }
};
</script>
<style scoped>
.com1{
  margin: 20px;
  background-color: #f00;
}
</style>
com2.vue
<template>
  <div>com2
    <p>age: {{age}}</p>
    <p>childCom2: {{ $attrs }}</p>
  </div>
</template>
<script>
export default {
  inheritAttrs: false,
  props: {
    age: String
  },
  created() {
    console.log('com2', this.$attrs);
    // { "name": "zhang", "gender": "女", "height": "158", "title": "程序员成长指北" }
  }
};
</script>
<style scoped>
div{
  background: #0f0;
  margin: 20px
}
</style>
查看原文

赞 5 收藏 4 评论 0

落霞与孤鹜齐飞 发布了文章 · 2019-03-30

WebSocket入门

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

DEMO如下

// client side file

let socket = new WebSocket('ws://localhost:9999');

// 当连接成功执行回调函数
socket.onopen = function () {
  console.log('客户端连接成功');
  
  // 向服务器发送一个消息
  socket.send('这是客户端发给服务器的消息');
}

// 绑定事件是用加属性的方式
socket.onmessage = function (res) {
  console.log('收到服务器端的响应是', res);
}
// server side file
/* npm i ws -S */
/* 用ws模块启动一个websocket服务器,监听 9999 端口 */
let WebSocketServer = require('ws').Server; 
let wsServer = new WebSocketServer({port: 9999});

wsServer.on('connection', socket=>{
    console.log('服务器连接成功');
    socket.on('message', msg=>{
        console.log('从客户端接收到的信息为'+msg);
        socket.send('这是从服务器发送到客服端的信息');
    })
})

简单模拟智能客服聊天

<template>
  <div class="wrap">
    <ul>
      <li v-for="(item, index) in dialogs" :key="index">
        <p :class="item.self ? 'fr': 'fl'">{{item.sendContent || item.acceptContent}}</p>
      </li>
    </ul>
    <div class="send-cont">
      <input v-model="sendCont">
      <input type="button" @click="send" value="发送">
    </div>
  </div>
</template>

<script>
let socket = new WebSocket("ws://localhost:9999");
export default {
  data() {
    return {
      dialogs: [],
      sendCont: "",
      isConnect: false
    };
  },
  mounted() {
    socket.onopen = () => {
      this.isConnect = true;
    };
  },
  methods: {
    send() {
      if (this.isConnect) {
        console.log("客户端连接成功");
        // 向服务器发送一个消息
        socket.send(this.sendCont);
        this.dialogs.push({ sendContent: this.sendCont, self: true });
        // 绑定事件是用加属性的方式
        socket.onmessage = res => {
          setTimeout(() => {
            this.dialogs.push({ acceptContent: res.data });
          }, Math.random() * 2000);
        };
      }
    }
  }
};
</script>

<style scoped>
* {
  padding: 0;
  margin: 0;
}
.fl {
  float: left;
}
.fr {
  float: right;
}
li::before {
  content: ".";
  display: block;
  height: 0;
  clear: both;
  visibility: hidden;
}

.wrap {
  position: relative;
  width: 380px;
  margin: auto;
  height: 600px;
  background-color: #eee;
  padding: 10px;
}
.wrap ul {
  overflow: auto;
  height: 550px;
}
li {
  list-style: none;
  margin: 2px;
}
li:nth-child(even) p {
  background-color: rgb(86, 107, 177);
}
li p {
  font-size: 20px;
  font-family: "Microsoft Yahei", serif, Arial, Helvetica, sans-serif;
  border-radius: 6px;
  padding: 4px;
  margin: 2px 4px;
  white-space: wrap;
  text-align: left;
}
li p.fr {
  background-color: rgb(61, 185, 30);
}

.send-cont {
  position: absolute;
  bottom: 10px;
  z-index: 999;
  width: 98%;
  margin: auto;
}
.send-cont input {
  display: inline-block;
  outline: none;
  border: 2px solid #080;
  border-radius: 6px;
  line-height: 30px;
  font-size: 16px;
  text-align: left;
}
.send-cont input:first-child {
  width: 70%;
}
.send-cont input[type="button"] {
  width: 20%;
  background-color: #080;
  color: #fff;
  text-align: center;
  padding: 1px;
}
</style>
const contents = ['你好', 'hi', 'hello', 'nice to meet you', 'how are you', 'how do you do']

let WebSocketServer = require('ws').Server; 
let wsServer = new WebSocketServer({port: 9999});

wsServer.on('connection', socket=>{
    console.log('服务器连接成功');
    socket.on('message', msg=>{
        console.log('从客户端接收到的信息为'+msg);
        socket.send(contents[parseInt(Math.random()*contents.length)]);
    })
})
查看原文

赞 18 收藏 15 评论 0

落霞与孤鹜齐飞 发布了文章 · 2018-12-25

由一个需求(广告每天弹一次)引发……

需求:用户每天第一次打开网页时弹出一条广告,并且可以选择关闭(默认当天不再弹出)和近期不再弹出。如何从头开始实现?

弹出框

这里用alert代替了,可以用插件如https://v3.bootcss.com/javascript/#popovershttp://element-cn.eleme.io/#/zh-CN/component/dialog等实现。

Cookie

前端设置方法
document.cookie = "date="+new Date(); // 创建一条cookie,浏览器关闭后就清理
document.cookie = "user=mine;expires=" + new Date().setDate(time.getDate() + 7); // 再创建一条cookie,7天后会自动清理
console.log(document.cookie); // user=mine; date=Thu Dec 20 2018 15:26:45 GMT+0800 (中国标准时间) */
自行封装setCookiegetCookieremoveCookie
function setCookie(name, value, expires){
    document.cookie = `${name}=${value};expires=${expires}`;
}
function getCookie(name){
    let cookies = document.cookie.split("; ");
    let values = [];
    for(var i=0; i<cookies.length; i++){
        values = cookies[i].split("=");
        if(values[0] === name){
            return values[1];
        }
    }
    return null;
}
function removeCookie(name){
    setCookie(name, "", "-1");
}
服务端设置方法

Http无状态协议,只能在同一个网站(包括多个页面)下获取,存储在客户端本地的一段信息,帮助我们存储信息获取信息。但是同样有风险:我们自己在浏览器上可以操作或者设置Cookie。

const express = require('express')
const cookieParser = require('cookie-parser')
const app = express()

app.use(cookieParser())

app.get('/', (req,res)=>{
    res.send('欢迎' + req.cookies.username); // 如果有username cookie则显示username,否则显示undefined
})

app.get('/login', (req,res)=>{
    let username = req.query.username;
    res.cookie('username',username,{maxAge:99999, httpOnly:true}); // maxAge:cookie的有效期;httpOnly设置为true,可以防止XSS攻击,只能被web serve访问,不能通过document.cookie获取
    res.send('登录成功');
})

app.listen(80);

至此,需求的思路就可以实现

// 选择关闭(默认当天不再弹出)
if(!getCookie("isAlert")){
    alert("我是弹出层");
    let now = new Date();
    setCookie("isAlert", "不要弹出了", new Date(now.getFullYear(), now.getMonth(), now.getDate(), "23", "59", "59"));
}
// 选择近期(7天)不再弹出
if(!getCookie("isAlert")){
    alert("我是弹出层");
    let now = new Date();
    setCookie("isAlert", "不要弹出了", new Date(now.getFullYear(), now.getMonth(), now.getDate()+7, "23", "59", "59"));
}

cookie的学习

同一网站共享一套cookie,它的数量和大小有限,有过期时间,JS中可以用document.cookie设置和访问。

  • 实现一个记住用户名(30天)和密码(7天)的需求
// html code
<form action="#" id="form">
    <input type="text" id="user">
    <input type="password" id="psd">
    <input type="submit" value="提交">
    <input type="button" id="clear" value="清除">
</form>
// js code
// setCookie getCookie removeCookie 前面以封装
var user = document.getElementById("user");
var psd = document.getElementById("psd");
var clear = document.getElementById("clear");
form.onsubmit = function(){
    var userTime = new Date();
    userTime.setDate(userTime.getDate() + 30);
    var psdTime = new Date();
    psdTime.setDate(psdTime.getDate() + 7);
    setCookie("user", user.value, userTime);
    setCookie("psd", psd.value, psdTime);
}
    user.value = getCookie('user');
    psd.value = getCookie('psd');
clear.onclick = function () {
    removeCookie('user');
    removeCookie("psd");
    user.value = "";
    psd.value = "";
};
  • jquery-cookie https://cdn.bootcss.com/jquery-cookie/1.4.0/jquery.cookie.min.js

    • 获取:$.cookie("name"),不存在就返回undefined,不管你看到的是什么,它都是字符串
    • 设置、修改:

      • $.cookie("name", "value") 有效期至当前会话关闭
      • $.cookie("name", "value", {expires: 7, path: "/"}) 有效期7天,有效路径是"/"
    • 删除:$.cookie("name", null);$.cookie("name", "", {expires: -1})

小技巧

  • 怎么获取当天最后一秒的时间戳
let now = new Date();
let resDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), "23", "59", "59").getTime();
  • 怎么获取N天后的此时
function N(N){
    let now = new Date();
    now.setDate(now.getDate() + N);
    return now;
}
  • 怎么获取本周最后一秒
let now = new Date();
let day = now.getDay() || 7;
/* 
    隐藏知识点:星期天返回的是0,typeof(now.getDay()) == "number" ,0 == false。
    实质是这段代码 day = now.getDay() === 0 ? 7 : now.getDay();
*/
let weekLastDate = now.getDate()+(7-day); // 如果以周六为最后一天,这里就是用6减
let resWeek = new Date(now.getFullYear(), now.getMonth(), weekLastDate, "23", "59", "59");

获取年和月的最后一秒就很简单了。

查看原文

赞 2 收藏 1 评论 0

落霞与孤鹜齐飞 发布了文章 · 2018-12-23

Ajax详解

Ajax

Ajax 全称是 asynchronous javascript and xml,并不是新的编程语言,可以说是已有技术的组合,主要用来实现客户端与服务器端的异步通信效果,实现页面的局部刷新,从而创建快速动态网页的技术。

Ajax过程

-  创建XMLHttpRequest对象,也就是创建一个异步调用对象
-  创建一个新的HTTP请求,并指定其请求的方法、URL及验证信息
-  设置响应 HTTP 请求状态变化的函数
-  发送 HTTP 请求
-  获取异步调用返回的数据
-  使用 JavaScript 和 DOM 实现局部刷新
var xhr = null; // 创建异步对象
if(window.XMLHttpRequest){
  xhr = new XMLHttpRequest(); // ie7+等现代浏览器
}else if(window.ActiveXObject){ // ie6,老版Opera
  xhr = new ActiveXObject('Microsft.XMLHTTP');
}
xhr.open('get','http://localhost:4000/test',true); // true是异步,可省略
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); // post 必须设置
xhr.onreadystatechange = function(){ // 若为同步,此代码不用写,直接在send后,用`xhr.responseText`即可。
    if(xhr.readyState==4 && xhr.status==200){
    /* 
    readyState
        0: 请求未初始化
        1: 服务器连接已建立
        2: 请求已接收
        3: 请求处理中
        4: 请求已完成,且响应已就绪
    status
        200 OK
        404 Not Found
    */
      xhr.responseText;
      xhr.responseXML.children[0].children;
      JSON.parse(xhr.responseText);
    }
}
xhr.send(String); // 用于post传参,形式:"a=1&b=2",而get传参就在url后面用“?”拼接

优缺点

  • 优点:可以使得页面不重载全部内容的情况下加载局部内容,降低数据传输量,避免用户不断刷新或者跳转页面,提高用户体验
  • 缺点:对搜索引擎不友好,要实现ajax下的前进后退功能成本较大;跨域问题限制

jQuery 中的 Ajax

全局ajax事件处理器

jQuery 库支持完整的 Ajax 操作。这里所包含的所有函数和方法用于从服务端加载数据,并且不会导致页面刷新

  • $(document).ajaxComplete(handler(event, XMLHttpRequest, ajaxOptions)) 当Ajax请求完成后注册一个回调函数。

    • 每当一个Ajax请求完成,jQuery就会触发ajaxComplete事件,在这个时间点所有处理函数会使用.ajaxComplete()方法注册并执行。
    • 从 jQuery 1.8 开始, .ajaxComplete() 方法只能绑定到 document元素。
    • 如果global选项设置为false,调用$.ajax() 或 $.ajaxSetup(),.ajaxComplete()方法将不会被触发。
  • 类似的还有:请求出错ajaxError()、请求发送前ajaxSend()、请求刚开始ajaxStart()、请求完成时ajaxStop()、请求成功完成时ajaxSuccess()

辅助函数

  • $.param() 创建一个数组或对象序列化的字符串,适用于一个URL地址查询字符串或Ajax请求。此方法无法对复杂数据结构进行编码。
  • serialize() 将用作提交的表单元素的值编译成字符串。
<form action="#">
    <input name="username" value="abc">
    <input name="age" value="24">
    <select name="sex">
        <option value="0">女</option>
        <option value="1">男</option>
    </select>
    <input type="submit" value="提交">
</form>

console.log($("form").serialize()); // username=abc&age=24&sex=0
  • serializeArray() 将用作提交的表单元素的值编译成拥有name和value对象组成的数组。

console.log($("form").serializeArray()); // [{name: "username", value: "abc"}, {name: "age", value: "24"}, {name: "sex", value: "0"}]

底层接口

  • $.ajax(url, {})$.ajax({url: ""}) 关于传参项(可以使用$.ajaxSetup()设置任何默认参数),下面列举一些常用的:

    • url:String,一个用来包含发送请求的URL字符串
    • data:Object、String,发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后面。
    • dataType:String,默认Intelligent Guess (xml, json, script, or html))。预期服务器返回的数据类型。

      • xmlhtmlscriptjsontext
      • jsonp: 以 JSONP 的方式载入 JSON 数据块。会自动在所请求的URL最后添加 "?callback=?"。默认情况下不会通过在URL中附加查询字符串变量 "_=[TIMESTAMP]" 进行自动缓存结果,除非将 cache参数设置为true。
    • accepts:PlainObject(可用$.isPlainObject()检测),内容类型发送请求头(Content-Type),用于通知服务器该请求需要接收何种类型的返回结果。如果accepts设置需要修改,推荐在$.ajaxSetup() 方法中设置一次。
    • async:Boolean,默认true,异步请求
    • global:Boolean,默认true。该请求是否触发全局处理事件(如$(document).ajaxComplete()等)
    • beforeSend(jqXHR, settings):Function,请求发送前的回调函数,用来修改请求发送前jqXHR,此功能可用来设置自定义 HTTP 头信息,在beforeSend函数中返回false将取消这个请求。
    • catch:Boolean,默认true,dataType为"script"和"jsonp"时默认为false。是否缓存此页面,原理是在GET请求参数中附加"_=时间戳"。该参数不是其他请求所必须的,除了在IE8中,当一个POST请求一个已经用GET请求过的URL。
    • complete:Function,请求完成后回调函数 (请求success 和 error之后均调用)。
    • success(data, textStatus, jqXHR):Function,请求成功时调用函数
    • error:Function,请求失败时调用函数
    • timeout:Number,设置请求超时时间(毫秒)。此设置将覆盖$.ajaxSetup() 里的全局设置。
    • jsonp:String,在一个jsonp请求中重写回调函数的名字。这个值用来替代在"callback=?"这种GET或POST请求中URL参数里的"callback"部分,比如{jsonp:'onJsonPLoad'}会导致将"onJsonPLoad=?"传给服务器。在jQuery 1.5,,设置jsonp选项为false,阻止了jQuery从加入"?callback"字符串的URL或试图使用"=?"转换。在这种情况下,你也应该明确设置jsonpCallback设置。例如, { jsonp: false, jsonpCallback: "callbackName" }
    • jsonpCallback:String、Function,为jsonp请求指定一个回调函数名。这个值将用来取代jQuery自动生成的随机函数名。这主要用来让jQuery生成一个独特的函数名,这样管理请求更容易,也能方便地提供回调函数和错误处理。你也可以在想让浏览器缓存GET请求的时候,指定这个回调函数名。从jQuery 1.5开始,你也可以使用一个函数作为该参数设置,在这种情况下,该函数的返回值就是jsonpCallback的结果。
    • contents: PlainObject,一个以"{字符串/正则表达式}"配对的对象,根据给定的内容类型,解析请求的返回结果。
    • contentType:String,默认application/x-www-form-urlencoded; charset=UTF-8。发送信息至服务器时内容编码类型。
    • context:Object,设置Ajax相关回调函数的上下文。即改变回调函数的this,默认就是传入的整个对象。
    • converters: PlainObject,默认: {"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML}。一个数据类型到数据类型转换器的对象。每个转换器的值是一个函数,返回经转换后的请求结果。
    • crossDomain:Boolean,同域请求为false, 跨域请求为true。如果你想在同一域中强制跨域请求(如JSONP形式),例如,想服务器端重定向到另一个域,那么需要将crossDomain设置为 true 。
    • username:String,于响应HTTP访问认证请求的用户名
    • password:String,用于响应HTTP访问认证请求的密码
  • $.ajax() 返回jqXHR对象,可调用

    • .done() success
    • .fail() error
    • .always() complete
    • .then() 传两个回调函数,分别是done和fail
  • $.ajaxPrefilter([dataTypes], handler(options, originalOptions, jqXHR) ) 在每个请求之前被发送和$.ajax()处理它们前处理,设置自定义Ajax选项或修改现有选项。
  • $.ajaxSetup(options) 为以后要用到的Ajax请求设置默认的值,其后的 AJAX 请求不再设置任何已设置的选项参数。

快捷方法

  • $.get() {type: "get"}
  • $.post() {type: "post"}
  • $.getScript() {type: "script"}
  • $.getJSON() {type: "json"}
  • $(selector).load() 从服务器载入数据并且将返回的 HTML 代码并插入至 匹配的元素中。

jQuery中jsonp

// 前端
function jsonpFn(data){
    console.log(data)
    console.log("jsonpFn");
}
$(function(){
    $.ajax({
        url: "http://localhost:3000/test",
        data: {username: "xx"},
        dataType: "jsonp",
        jsonp: "selfNamedReplaceCallback",
        jsonpCallback: "jsonpFn", // server side:req.query.callback = "jsonpFn"
        success(data){
            console.log(data)
        }
    })
})

// 后端
const app = require('express').Router();
app.get('/test', (req, res, next) => {
    let name = req.query.username;
    let callback = req.query.selfNamedReplaceCallback
    let s1 = "{status: 1}"
    let s2 = `
        {
            status: 0,
            info: {
                name: "${name}",
                age: 24,
                sex: "girl",
                tel: "1522*******"
            }
        }
    `
    s1=callback+"("+s1+")"
    s2=callback+"("+s2+")"
    if(!name)return res.send(s1)
    res.send(s2)
}).listen(3000)

Axios

  • axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF

在vue项目中使用axios 的 Demo

// main.js
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost/'
Vue.prototype.$axios = axios
// 在 .vue 文件(组件)
this.$axios.get("").then().catch()

this.$axios.all([
    this.$axios.post('one','content=123'),
    this.$axios.get('one?pageIndex=1')
])
.then(this.$axios.spread((res1,res2)=>{
    console.log(res1,res2)
}))
.catch(err=>{
    console.log(err)
})

这里分发请求,同时请求成功才执行 then 方法,可用于获取 省市区 的数据

// 在 server.js 文件
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
const router = new Router()
router.get('/one',ctx=>{
    ctx.body = {
        a: 1,
        b: 2
    }
})
router.post('/one',ctx=>{
    ctx.body = {
        c: 3,
        d: 4
    }
})

app.use((ctx,next)=>{
    ctx.set("Access-Control-Allow-Origin", "*");
    ctx.body = {}
    next()
})
.use(router.routes())
.use(router.allowedMethods())
.listen(80)
拦截器
axios.interceptors.request.use(function(ops){
    console.log(ops);
    ops.headers = {/* ... */}; // 请求前修改
    return ops; // 返回没有修改的位置,如果return false 直接拦截
})

例如,在请求成功前显示加载loading图标,拿到数据后消失

// main.js
import Mint from 'mint-ui' // 实际项目中选择引入
import 'mint-ui/lib/style.css'
Vue.use(Mint)

axios.interceptors.request.use(function(config){
    Mint.Indicator.open()
    return config;
})
axios.interceptors.response.use(function(data){
    Mint.Indicator.close()
    return data;
})

Ajax、jQuery.ajax、Axios和Fetch的区别

  • ajax最早出现的发送后端请求技术,利用用XMLHttpRequest对象。
  • $.ajax是jQuery中的发送后端请求技术,基于原生Ajax的封装。
  • Axios不是原生JS的,需要进行安装。它在client-side和server-side都可以使用。也可以在请求和响应阶段进行拦截。它是基于promise对象的。
  • Fetch号称是AJAX的替代品,使用了ES6中的promise对象。其参数有点像jQuery.ajax。但是fetch不是对ajax的封装,而是原生js实现的,并没有使用XMLHttpRequest对象。
查看原文

赞 20 收藏 16 评论 0

落霞与孤鹜齐飞 发布了文章 · 2018-12-09

对象、原型与原型链

Object.defineProperty
let obj = {
    key0: 0
}
Object.defineProperty(obj, "key1", {
    value: "1",
    writable: false, // 是否可写,默认false。obj.key1="1.0"; 不可写,不起作用
    configurable: false, // 是否可以再次配置,默认false。不能再设置value,writable,configurable等属性
    enumerable: false // 是否可枚举,默认false。不能在(for...in)中遍历
})
console.log(Object.getOwnPropertyDescriptor(obj, "key0")); // {value: 0, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj, "key1")); // {value: "1", writable: false, enumerable: false, configurable: false}
判断对象是否有指定属性或方法而不是继承的

obj.hasOwnProperty("toString")

获取对象属性的数组
Object.getOwnPropertyNames(obj)

Object.keys(obj) // 获取不到不可枚举(enumerable: false)的属性
Object.assign

assign() 用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。同 $.extend();

Object.assign({}, obj); // {key0: "0"}
$.extend({}, obj); // {key0: "0"}
对象和JSON的转化
let xmObj = {
    name: "xiaoming",
    age: 20,
    sex: "男",
    isMarry: false
}
// 序列化成JSON
var res = JSON.stringify(xmObj, null, '  '); // typeof res == "string"
// 解析成对象
var resO = JSON.parse(res); // typeof resO == "object"
先看一段代码
function Person(name, age){
    this.name = name;
    this.age = age;
}
Person.prototype.work=function(){}

function Programmer(name,age){
    Person.call(this,name,age);
}
Programmer.prototype = new Person();
Programmer.prototype.code=function(){}; // 如果写成对象会覆盖继承来的属性和方法,即赋值为{...}。

let example = new Programmer('码农',24); // 创建实例,example是实例,Programmer是原型。

Object.prototype.sth = function(){}
new的过程:创建一个空对象,让this指向它,通过this.name,this.age等赋值,最终返回this。
原型和实例

在上面代码中,Programmer是原型,example是它的实例。用instanceof检测,有
example instanceof Programmer === true
example instanceof Person === true
example instanceof Object === true

通过example.constructor属性返回对创建此对象的数组函数的引用。
example.constructor===Person
example.constructor===Person.prototype.constructor
但是constructor 属性易变,不可信赖,它可以通过修改prototype而手动修改。

实例的__proto__对应原型的prototype
example.__proto__===Programmer.prototype
example.__proto__.__proto__===Person.prototypeProgrammer.prototype.__proto__
example.__proto__.__proto__.__proto__===Object.prototype
所有对象都是Object的实例,并继承Object.prototype的属性和方法。

原型链

找一个属性,首先在example.__proto__去找,如果没有,再去example.__proto__.__proto__找,……,再到Object.prototype,一直到null,即Object.prototype.__proto__ === null。这个串起来的链就是原型链。
比如:example.codeexample.workexample.doSthexample.toString都有,而example.foo就为undefined

查看原文

赞 0 收藏 0 评论 0

落霞与孤鹜齐飞 发布了文章 · 2018-11-30

Vuex

Vuex 是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 和单纯的全局对象有以下两点不同

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  • 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

回顾组件

Index.vue

<template>
    <div id="app">
        <product-list-one :products="products"></product-list-one>
        <product-list-two :products="products"></product-list-two>
    </div>
</template>

<script>
import ProductListOne from './ProductListOne'
import ProductListTwo from './ProductListTwo'
export default {
    name: 'Index',
    components: {
        'product-list-one': ProductListOne,
        'product-list-two': ProductListTwo
    },
    data(){
        return {
            products: [
                {name: 'apple', price: 200},
                {name: 'pear', price: 140},
                {name: 'banana', price: 20},
                {name: 'mango', price: 10},
            ]
        }
    }
}
</script>

ProductListOne

<template>
    <div>
        <h2>1</h2>
        <ul>
            <li v-for="product in products" :key="product+Math.random()*Date.now()">
                <span class="name">{{product.name}}</span>
                <span class="price">{{product.price}}</span>
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    props: ['products'],
    data(){
        return {

        }
    }
}
</script>

<style scoped>
    div{
        font-weight: bold;
        color: #f0f;
    }
</style>

ProductListTwo

<template>
    <div>
        <h2>2</h2>
        <ul>
            <li v-for="product in products" :key="product+Math.random()*Date.now()">
                <span class="name">{{product.name}}</span>
                <span class="price">{{product.price}}</span>
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    props: ['products'],
    data(){
        return {

        }
    }
}
</script>

store.js

state

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        products: [
            {name: 'apple', price: 200},
            {name: 'pear', price: 140},
            {name: 'banana', price: 20},
            {name: 'mango', price: 10},
        ]
    }
})

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import {store} from './store'

new Vue({
  el: '#app',
  router,
  store,
  components: {App},
  template: '<App/>'
})

Index.vue

<template>
    <div id="app">
        <product-list-one></product-list-one>
        <product-list-two></product-list-two>
    </div>
</template>

<script>
import ProductListOne from './ProductListOne'
import ProductListTwo from './ProductListTwo'
export default {
    name: 'Index',
    components: {
        'product-list-one': ProductListOne,
        'product-list-two': ProductListTwo
    },
    data(){
        return {
            // 把数据移到store的state里了
        }
    }
}
</script>

ProductListOne.vue

<template>
    <div>
        <h2>1</h2>
        <ul>
            <li v-for="product in products" :key="product+Math.random()*Date.now()">
                <span class="name">{{product.name}}</span>
                <span class="price">{{product.price}}</span>
            </li>
        </ul>
    </div>
</template>

<script>
export default {
    computed: {
        products(){
            // 接收store的数据
            return this.$store.state.products;
        }
    },
    data(){
        return {
            
        }
    }
}
</script>

改变名字,价格打折
ProductListTwo.vue

v-for="product in saleProducts"

computed: {
    saleProducts(){
        return this.$store.state.products.map(product=>{
          return  {
              name: `**${product.name}**`,
              price: product.price/2
            }
        });
    }
}

如果在组件ProductListOne.vue也要改名打折,复制即可,但成百上千个组建也需要呢?就用到getters了。

getters

store.js

getters: {
    saleProducts(state){
        return state.products.map(product=>{
          return  {
              name: `**${product.name}**`,
              price: product.price/2
            }
        });
    }
}

ProductListOne.vue或其他需要用到的组件

computed: {
    saleProducts(){
        return this.$store.getters.saleProducts;
    }
}

Mutations

store.js

mutations: {
    reducePrice: state=>{
        state.products.forEach(p=>{
            p.price -=1;
        })
    }
}

组件的methods

<button @click="reductPrice">降价</button>

reducePrice(){
    this.$store.commit('reducePrice')
}

Actions

如果把mutations改为异步,即

mutations: {
    reducePrice: state=>{
        setTimeout(function(){
            state.products.forEach(p=>{
                p.price -=1;
            })
        },3000)
    }
}

这样在页面上呈现效果(3000ms后)和Vue Devtools Vuex调试(立即)不一致。但是在actions里面就可以了。

actions: {
    reducePrice: context => {
        setTimeout(function(){
            context.commit('reducePrice'); // mutations的reducePrice
        },3000)
    }
}
传参数

mutations 和 actions

mutations: {
    reducePrice: (state,num)=>{
        state.products.forEach(p=>{
            p.price -=num;
        })
    }
},
actions: {
    reducePrice: (context,num) => {
        setTimeout(function(){
            context.commit('reducePrice',num); // mutations的reducePrice
        },3000)
    }
}

组件

<button @click="reducePrice(4)">降价</button>
4是实参

methods: {
    reducePrice(amount){ // amount是形参
        this.$store.dispatch('reducePrice',amount)
    }
}

如果 actions 里面有多个方法呢,不可能完全在methods里面列举,就可以在组件中这样解决。

import {mapGetters, mapActions} from 'vuex'

export default{
    computed: {
        ...mapGetters(['saleProducts'])
    },
    methods: {
        ...mapActions(['reducePrice'])
    }
}

https://vuex.vuejs.org/zh/

查看原文

赞 2 收藏 0 评论 0

落霞与孤鹜齐飞 发布了文章 · 2018-11-30

Array 的一些常用 API

unshift、push、shift、pop

  • 这4个方法都会改变原数组
  • unshift() 从前面添加元素,push() 从后面追加元素,返回数组长度
  • shift() 删除第0个元素,pop() 删除最后一个元素,返回被删除的元素

slice

  • 不改变原数组
  • slice(m, n)返回原数组索引m(包含)到n(不包含)的元素数组。不传参数默认全部截取,只传一个参数,从该位置截取到末位。类似于String.prototype.substring
let arr = [1,2,3,4,5]
console.log(arr.slice()); // [ 1, 2, 3, 4, 5 ]
console.log(arr.slice(1)); // [ 2, 3, 4, 5 ]
console.log(arr.slice(2,4)); // [ 3, 4 ]
console.log(arr); // [1,2,3,4,5]

splice

  • 改变原数组。
  • splice(m,n,e1,e2,e3) 从索引m(包括)到n(不包括)的元素删除数组,再在该位置处添加e1,e2,e3。若n传入0,则只增加;若只传m和n,则只删除;若只传m,则从m位置删除到末位。放回删除元素数组
let arr = [1,2,3,4,5];
console.log(arr.splice(2), arr); // [ 3, 4, 5 ] [ 1, 2 ]
console.log(arr.splice(0,1,2), arr); // [ 1 ] [ 2, 2, 3, 4, 5 ]

concat

  • 拼接,不改变原数组
let arr = [1,2,3], arr1 = [4];
console.log(arr.concat(arr1, 5), arr); // [ 1, 2, 3, 4, 5, ] [ 1, 2, 3 ]
console.log([...arr, ...arr1, 5]); // [ 1, 2, 3, 4, 5 ] (对象也可以这样拼接,但重复的会覆盖,相当与 Object.assign)

of 和 from

  • of()类似于new Array(),但后者如果传入一个参数,则是设置数组长度。
  • from() 把伪数组转换为真数组,类似于[].slice.call()(或者写成Array.prototype.slice.call())
  • 伪数组有DOM集、arguments、{0: 'zero', 1: 'one', length: 2}
Array.of(1,2,3,4); // [ 1, 2, 3, 4 ]
Array.from({0: 'zero', 1: 'one', length: 2}); // [ 'zero', 'one' ]
判断数组的方法有
  • xx instanceof Array
  • Array.isArray()
  • Object.prototype.toString.call() === '[object Array]'
这里补充一点判断类型的技巧:
  • typeof操作符可以判断出number、boolean、string、function和undefined,而不能判断处具体的Object类型。

    • 判断Array要使用Array.isArray(arr);
    • 判断null请使用myVar === null;
    • 判断某个全局变量是否存在用typeof window.myVar === 'undefined';
    • 函数内部判断某个变量是否存在用typeof myVar === 'undefined'
  • 不必把任意类型转换为boolean再判断,因为可以直接写if (myVar) {...},如果需要转换可以用 !!myVar
  • 不要使用new Number()、new Boolean()、new String() 创建包装对象;
  • 用parseInt()或parseFloat()来转换任意类型为Number;用String()来转换任意类型为String,或者直接调用某个对象的toString()方法;

indexOf 和 includes

  • indexOf() 返回索引,不存在就返回 -1。inclues()返回布尔值。
  • NaN 不能通过indexOf()判断,它是通过“===”比较的。
arr = [1, '2', null, NaN];
arr.indexOf(NaN); // -1
arr.includes(NaN); // true

filter、find 和 findIndex

  • filter() 返回数组。find() 返回值,不存在就返回 undefined。 findIndex() 返回第一个匹配到的索引,不存在就返回 -1。
// let arr = [1, '2', null, NaN];
// arr.filter(item => typeof item === 'number'); // [1, NaN]
// arr.find(item => typeof item === 'number'); // 1
// arr.findIndex(item => typeof item === 'number'); // 0

some 和 every

  • 返回布尔值
  • some()用于检测数组中的元素是否有满足指定条件的;every()用于检测数组中所有元素是否都符合指定条件。
  • 不对空数组进行检测,不改变原数组
let arr = [Array(), [], {}, null]
let res1 = arr.some((item, index, arr) => {
    return Object.prototype.toString.call(item) === '[object Array]'
})
let res2 = arr.every((item, index, arr) => {
    return Object.prototype.toString.call(item) === '[object Array]'
})
console.log(res1,res2); // true false

map 和 forEach

  • map() 对数组中的每个元素进行处理,得到新的数组,不改变原数组
  • forEach() 相当于 for 循环,返回 undefined,不改变原数组
let res = [0,1,2,3,4].map((item, idx)=> item * idx); // [ 0, 1, 4, 9, 16 ]

reduce

  • reduce((accumulator, currentValue, currentIndex, array)=>{}, initValue)
  • 第一个参数是迭代器函数,函数的作用是对数组中从左到右的每一个元素进行处理。第二个可选参数是累加器的初始值。没有时,累加器第一次的值为currentValue。
  • accumulator 累加器,即函数上一次调用的返回值。第一次的时候为 initialValue || arr[0]
  • currentValue 数组中函数正在处理的的值,第一次的时候是 initialValue || arr[1]
  • currentIndex 数组中函数正在处理的的索引
  • array 函数调用的数组
console.log(
    [1,2,3].reduce((a,b,c,d)=>{
        console.log(a,b,c,d);
        return a+b; // 下一次的a
    }, 4)
); // 10
/* 
    4 1 0 [ 1, 2, 3 ]
    5 2 1 [ 1, 2, 3 ]
    7 3 2 [ 1, 2, 3 ] 
*/

当然累乘或者做其他业务也是可以的。

查看原文

赞 0 收藏 0 评论 0

落霞与孤鹜齐飞 发布了文章 · 2018-11-17

跨域解决之JSONP和CORS

相同协议、域名、端口下

  • 页面在 http://localhost:3000/0
  • 服务在 http://localhost:3000/1
  • 控制台能正常输出 {name: '', sex: '', _stamp: ''}

views/0.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>

    </style>
</head>
<body>
    <form action="/" onsubmit="return false">
        <label for="name">NAME</label><input type="text" id="name" name="name"><br>
        <label for="sex">SEX</label><input type="text" id="sex" name="sex"><br>
        <input type="submit" value="SUBMIT">
    </form>
    <script data-original="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
        $('form').on('submit', function(){
            $.ajax({
                url: 'http://localhost:3000/1',
                data: {
                    name: $('#name').val(),
                    sex: $('#sex').val()
                },
                success: function(data){
                    console.log(data);
                }
            })
        })
    </script>
</body>
</html>

serve.js

const server = require('express')();

server.set('view engine', 'ejs');

server.get('/0', (req, res) => {
    res.render('0.ejs', {});
})

server.get('/1', (req, res) => {
    let {name, sex} = req.query;
    res.send({name, sex, _stamp: + new Date});
})

server.listen(3000);

JSONP

  • 0.ejs改为0.hmtl,直接打开或者在http://localhost:3001/views/0.html打开(3000端口被占用时运行$ browser-sync start --server --files '**'
  • 控制台报错 Access to XMLHttpRequest at 'http://localhost:3000/1?name=&sex=' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

jQuery 的 JSONP

0.html 的脚本改为

function jCb(data){ // 这个函数和ajax 的 success 同样可以操作 data
    console.log('你要的', data);
}
$('form').on('submit', function(){
    $.ajax({
        url: 'http://localhost:3000/1',
        data: {
            name: $('#name').val(),
            sex: $('#sex').val()
        },
        dataType: 'jsonp',
        jsonpCallback: 'jCb', // 服务端 req.query.callback = 'jCb'
        success: function(data){
            console.log(data);
        }
    })
})

server.js 注意返回客户端的字符串的拼接

const server = require('express')();
server.get('/1', (req, res) => {
    let {name, sex, callback} = req.query;
    var data = "{name: '', sex: '', _stamp: ''}"; // 然后再在单引号处拼接
        data = "{name: '" + name + "', sex: '" + sex + "', _stamp:' " + Date.now() + "'}";
        data = `{name: '${name}', sex: '${sex}', _stamp: '${+ new Date}'}`

    var resStr = callback + "()"
        resStr = callback + "(" + data + ")"
    console.log(callback, typeof callback); // jCb string
    console.log(resStr, typeof resStr); // jCb({name:'',sex:'',_stamp:'1542456915800'}) string
    res.send(resStr);
})

server.listen(3000);

利用 script 标签

0.html

<script>
    function jCb(data) {
        console.log("jsonpCallback: " + data.name)
    }
</script>
<script src = 'http://localhost:3000/1?jsonp=jsonpCallback'></script>

server.js

server.get('/1', (req, res) => {
    var data = 'var data = {name: $("#name").val(), sex: $("#sex").val(), _stamp: + new Date };'
    var debug = 'console.log(data);'
    var callback = '$("form").submit(function(){' + data + req.query.jsonp + '(data);' + debug + '});'
    res.send(callback);
})

CORS

html 代码还是最初的代码,server.js 改变

server.get('/1', (req, res) => {
    // 设置可以请求的域名,"*" 代表所有域名
    res.header("Access-Control-Allow-Origin", "http://localhost:3001");

    // 设置所允许的HTTP请求方法。
    res.header("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE");

    // 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段。
    res.header("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type");

    // 服务器收到请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

    // Content-Type表示具体请求中的媒体类型信息。
    res.header("Content-Type", "application/json;charset=utf-8");

    // 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。
    // 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*"。
    res.header("Access-Control-Allow-Credentials", true);

    // 该字段可选,用来指定本次预检请求的有效期,单位为秒。
    // 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证
    // 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证
    res.header("Access-Control-Max-Age", 300);

    /*
    CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
        Cache-Control、
        Content-Language、
        Content-Type、
        Expires、
        Last-Modified、
        Pragma。
    */
    // 需要获取其他字段时,使用Access-Control-Expose-Headers,
    // getResponseHeader('myData')可以返回我们所需的值
    res.header("Access-Control-Expose-Headers", "myData");


    let {name, sex} = req.query;
    res.send({name, sex, _stamp: + new Date});
})
查看原文

赞 0 收藏 0 评论 0

落霞与孤鹜齐飞 发布了文章 · 2018-10-24

项目中常用的ES6

看代码及注释就懂了

把ES6(es2015)代码转换为ES5代码

  • $ npm install -g babel-cli
  • $ npm install babel-preset-env --save
  • $ babel ./src/es6.js -w --out-file ./dist/es5.js --presets env

解构赋值

const breakfast = () => ['cake', 'coffee', 'apple']
let [dessert, drink, fruit] = breakfast()
console.info(dessert, drink, fruit) // 'cake' 'coffee' 'apple'
const breakfast = () => {
    return {
        dessert: 'cake',
        drink: 'coffee',
        fruit: 'apple'
    }
}
let {dessert, drink, fruit} = breakfast()
console.info(dessert, drink, fruit) // 'cake' 'coffee' 'apple'

字符串

  • 反引号代替引号:`some content ${variable} some content`
  • str.includes()、str.startsWidth()、str.endsWith() 代替了正则表达式匹配

数组展开符

// 利用 Array 的 concat() 实现
let breakfast = ['cake', 'coffee', 'apple']
let food = ['rice', ...breakfast]
console.info(...breakfast, food) // 'cake' 'coffee' 'apple', ["rice", "cake", "coffee", "apple"]

对象

// 对象属性
let food = {}
let drink = 'hot drink'
food[drink] = 'milk'
console.log(food['hot drink']) // 'milk'
let food = {}
let drink = 'hot drink'
food[drink] = 'milk'

let dessert = 'cake'
food.fruit = 'banane'

let breakfast = Object.assign({}, {dessert}, food) // {dessert} 有属性有值
console.log(breakfast) // {dessert: "cake", hot drink: "milk", fruit: "banane"}

集合 Set 和 Map

let fruit = new Set(['apple', 'banane', 'orange'])
/* 添加元素,但是不会添加相同的元素,利用这个特性实现数组去重:[...new Set(arr)] */
fruit.add('pear')
/* 元素数量 */
console.log(fruit.size) // 4
/* 是否有指定元素 */
console.log(fruit.has('apple')) // true
/* 删除元素 */
fruit.delete('banana') 

console.log(fruit) // Set(4) {"apple", "banane", "orange", "pear"}
/* 遍历 */
fruit.forEach(item => {
    console.log(item)
})
/* 清空 */
fruit.clear()

console.log(fruit) // Set(0) {}
let food = new Map()
let fruit = {}, cook = function(){}, dessert = '甜点'
food.set(fruit, 'apple')
food.set(cook, 'knife')
food.set(dessert, 'cake')

console.log(food, food.size) // Map(3) {{…} => "apple", ƒ => "knife", "甜点" => "cake"} 3

console.log(food.get(fruit)) // "apple"
console.log(food.get(dessert)) // "cake"

food.delete(dessert)
console.log(food.has(dessert)) // false

food.forEach((value, key) => {
    console.log(`${key} = ${value}`) // [object Object] = apple    function(){} = knife
})

food.clear()
console.log(food) // Map(0) {}

Generator

function* calc(num){
    yield num+1
    yield num-2
    yield num*3
    yield num/4
    return num
}
let cal = calc(4)
console.info(
    cal.next(), // {value: 5, done: false}
    cal.next(), // {value: 2, done: false}
    cal.next(), // {value: 12, done: false}
    cal.next(), // {value: 1, done: false}
    cal.next(), // {value: 4, done: true} 遍历完了后 done:true
    cal.next() // {value: undefined, done: true}
)

class Chef{
    constructor(food){
        this.food = food;
        this.dish = [];
    }
    get menu(){ // 不得有参数(Getter must not have any formal parameters.)
        console.log('getter 取值')
        console.log(this.dish)
        return this.dish
    }
    set menu(dish){ // 必须有一个参数(Setter must have exactly one formal parameter.)
        console.log('setter 存值')
        this.dish.push(dish)
    }
    cook(){
        console.log(this.food)
    }
}
let Gu = new Chef('vegetables');
Gu.cook() // "vegetables"
console.log(Gu.menu) // "get 取值" []

Gu.menu = 'egg' // "setter 存值"
Gu.menu = 'fish' // "setter 存值"

console.log(Gu.menu); // "getter 取值" ["egg", "fish"]
class Person {
    constructor(name, birth){
        this.name = name
        this.birth = birth
    }
    intro(){
        return `${this.name}的生日是${this.birth}`
    }
}

class One extends Person {
    constructor(name, birth){
        super(name, birth)
    }
}

let z = new Person('zz', '01-09')
let x = new One('xx', '01-09')

console.log(z.intro(), x.intro()) // "zz的生日是01-09" "xx的生日是01-09"
// 转化为ES5的代码大概就是
/* function Person(name, birth) {
    this.name = name;
    this.birth = birth;
}
Person.prototype.intro = function () {
    return this.name + '\u7684\u751F\u65E5\u662F' + this.birth;
};
function One(name, birth) {
    Person.apply(this, arguments);
}
One.prototype = new Person();
var z = new Person('zz', '01-09');
var x = new One('xx', '01-09');

console.log(z.intro(), x.intro()); // "zz的生日是01-09" "xx的生日是01-09" */

Promise

  • 回调地狱
function ajax(fn){
    setTimeout(()=>{
        console.log('执行业务')
        fn()
    },1000)
}
ajax(()=>{
    ajax(()=>{
        ajax(()=>{
            ajax(()=>{
                console.log('执行结束4')
            })
            console.log('执行结束3')
        })
        console.log('执行结束2')
    })
    console.log('执行结束1')
})
/*
    效果:
        1s: 执行业务 执行结束1
        2s:  执行业务 执行结束2
        3s: 执行业务 执行结束3
        4s:  执行业务 执行结束4
*/
  • Promise 这样写
function delay(word){
    return new Promise((reslove,reject) => {
        setTimeout(()=>{
            console.log('执行业务')
            reslove(word)
        },1000)
    })
}
delay('执行业务1')
    .then(word=>{
        console.log(word)
        return delay('执行业务2')
    })
    .then(word=>{
        console.log(word)
        return delay('执行业务3')
    })
    .then(word=>{
        console.log(word)
        return delay('执行业务4')
    })
    .then(word=>{
        console.log(word)
    })
    .catch()
  • async + await
function delay(word){
    return new Promise((reslove, reject) => {
        setTimeout(()=>{
            console.log('执行业务')
            reslove(word)
        },1000)
    })
}

const start = async () => {
    const word1 = await delay('执行业务1')
    console.log(word1)

    const word2 = await delay('执行业务2')
    console.log(word2)

    const word3 = await delay('执行业务3')
    console.log(word3)
    
    const word4 = await delay('执行业务4')
    console.log(word4)
}

start()
查看原文

赞 4 收藏 2 评论 0

认证与成就

  • 获得 62 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-05-31
个人主页被 535 人浏览