vue前端面试题分享

常问点

1.自己封装一个指定的派发功能,派发指定组件的方法 (如 elementUI 的$dispatch 实现)

  • 实现 $dispatch 方法
  • 实现 $broadcast
// 一直向上找parent 触发方法
// 如果我想触发指定组件的 方法
Vue.prototype.$dispatch = function (eventName, conponmentName, value) {
  let parent = this.$parent;
  while (parent) {
    if (parent.$options.name === conponmentName) {
      parent.$emit(eventName, value);
      return;
    }
    parent = parent.$parent;
  }
};

2. vue 修饰符 sync 的作用

🍁 当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定 🍁

  • 🌰 从 2.3.0 起我们重新引入了 .sync 修饰符,但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器
<comp :foo.sync="bar"></comp>

会被拓展为

<comp :foo="bar" @update:foo="val => bar = val"></comp>00

当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:

this.$emit("update:foo", newValue);
  • 🍊 当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定。如果我们不用.sync,我们想做上面的那个弹窗功能,我们也可以 props 传初始值,然后事件监听,实现起来也不算复杂。这里用 sync 实现,只是给大家提供一个思路,让其明白他的实现原理,可能有其它复杂的功能适用 sync。

这只是一个简单的例子,看完这个不知你是不觉得有个指令跟这个很相似,v-model?对,就是 v-model 在组件上使用的时候。

3. vue keep-alive 常用属性

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。(用逗号分隔字符串、正则表达式或一个数组)
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
  • max - 数字。最多可以缓存多少组件实例。

4. vue 运行机制,依赖收集

🍂 Vue 能够实现当一个数据变更时,视图就进行刷新,而且用到这个数据的其他地方也会同步变更;而且,这个数据必须是在有被依赖的情况下,视图和其他用到数据的地方才会变更。 所以,Vue 要能够知道一个数据是否被使用,实现这种机制的技术叫做依赖收集 🍂

  • 每个组件实例都有相应的 watcher 实例 - 渲染组件的过程,会把属性记录为依赖 - 当我们操纵一个数据时,依赖项的 setter 会被调用,从而通知 watcher 重新计算,从而致使与之相关联的组件得以更新 🍉
  1. 🌰 既然模板渲染需要用到某个数据,那么一定会对这个数据进行访问,所以只要拦截 getter,就有时机做出处理
  2. 🌰 在值变更的时候,也有 setter 可供拦截,那么拦截 setter,也就能做出下一步动作。

所以在 getter 里,我们进行依赖收集(所谓依赖,就是这个组件所需要依赖到的数据),当依赖的数据被设置时,setter 能获得这个通知,从而告诉 render()函数进行重新计算。🍓🍓🍓

vue-watcher.png

5. 一个页面有父子组件,进入之后的渲染顺序触发的生命周期是什么样的

  1. 组件同步引入时生命周期顺序为

    父组件的 beforeCreate、created、beforeMount --> 所有子组件的 beforeCreate、created、beforeMount --> 所有子组件的 mounted --> 父组件的 mounted 🍊

  2. 组件异步引入时生命周期顺序为

    父组件的 beforeCreate、created、beforeMount、mounted --> 子组件的 beforeCreate、created、beforeMount、mounted 🍊

vue-render.png

6. keep-alive,如果只想要 router-view 里面的某个组件被缓存,怎么做

  • 使用 include/exclude keep-alive 包裹的是 component 对象

    router-view 也是一个组件

  • 增加 router.meta 属性

// routes 配置
export default [
  {
    path: "/",
    name: "home",
    component: Home,
    meta: {
      keepAlive: true, // 需要被缓存
    },
  },
  {
    path: "/:id",
    name: "edit",
    component: Edit,
    meta: {
      keepAlive: false, // 不需要被缓存
    },
  },
];
<keep-alive>
  <router-view v-if="$route.meta.keepAlive">
    <!-- 这里是会被缓存的视图组件,比如 Home! -->
  </router-view>
</keep-alive>

<router-view v-if="!$route.meta.keepAlive">
  <!-- 这里是不被缓存的视图组件,比如 Edit! -->
</router-view>

7. 假设这里有 3 个路由: A、B、C,默认显示 A,当 从 B 跳到 A,A 不刷新,C 跳到 A,A 刷新

  • 使用到 组件内守卫 beforeRouteLeave 在跳转下一个路由之前改变 route.meta 属性值
  • B 组件内的设置
export default {
  data() {
    return {};
  },
  methods: {},
  beforeRouteLeave(to, from, next) {
    // 设置下一个路由的 meta
    to.meta.keepAlive = true; // 让 A 缓存,即不刷新
    next();
  },
};
  • C 组件内设置
export default {
  data() {
    return {};
  },
  methods: {},
  beforeRouteLeave(to, from, next) {
    // 设置下一个路由的 meta
    to.meta.keepAlive = false; // 让 A 不缓存,即刷新
    next();
  },
};

8. 组件通信中的 eventbus 原理是什么

EventBus 又称为事件总线。在 Vue 中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件

  • 创建 全局 EventBus
const EventBus = new Vue();

Object.defineProperties(Vue.prototype, {
  $bus: {
    get: function () {
      return EventBus;
    },
  },
});

或者使用 vue.prototype.$bus = new Vue();

  • 使用 发布/订阅模式
this.$bus.$emit('nameOfEvent',{ ... pass some event data ...});

this.$bus.$on('nameOfEvent',($event) => {
    // ...
})

$once 只执行一次

9. <font color=red>eventbus 的使用缺点</font>

  • vue 作为单页面应用,如果在某一个页面刷新了之后,与之相关的 EventBus 会被移除
  • 如果业务有反复操作的页面,EventBus 在监听的时候就会触发很多次,也是一个非常大的隐患。这时候我们就需要好好处理 EventBus 在项目中的关系。通常会用到,在 vue 页面销毁时,同时移除 EventBus 事件监听
import EventBus from "../assets/EventBus";
EventBus.$off("sendMessage", 事件函数); // 移除指定事件
EventBus.$off("sendMessage"); // 移除应用内所有对此事件的监听
EventBus.$off(); // 移除应用内所有事件的监听
  • 由于是都使用一个 Vue 实例,所以容易出现重复触发的情景,两个页面都定义了同一个事件名,并且没有用$off 销毁(常出现在路由切换时)。

10. class 实现

  • es5 通过构造函数(class 只是一个语法糖)
class Mold {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }
  count() {
    return this.a + this.b;
  }
}
let sum = new Mold(1, 2);
console.log(sum.count()); //3

对应的 es5 中的构造函数的写法

function Mold(a, b) {
  this.a = a;
  this.b = b;
}
Mold.prototype.count = function () {
  return this.a + this.b;
};
let sum = new Mold(1, 2);
console.log(sum.count()); //3

11. 实现继承的几种方式

1. 原型链继承

将父类的实例作为子类的原型

function Son(name) {
  // Father.call(this);
  this.name = name || "->son";
}
Son.prototype = new Father();
var s = new Son("son");
console.log(s.name); // son
s.sayAge(); // 18
s.sayName(); // son
console.log(s.age); // 18
console.log(s instanceof Father); // true
console.log(s instanceof Son); // true
  • 优点
    • 纯粹的继承关系,实例是子类的实例,也是父类的实例
    • 父类新增原型方法、原型属性,子类都能访问到
    • 简单,易于实现
  • 缺点
    • 可以在 Son 构造函数中,为 Son 实例增加实例属性。新增的属性和方法必须放在 new Father()这样的语句之后执行。
    • 无法实现多继承,因为原型一次只能被一个实例更改
    • 来自原型对象的所有属性被所有实例共享
    • 创建子类实例时,无法向父构造函数传参

2. 构造继承(call):复制父类的实例属性给子类

function Son(name) {
  Father.call(this);
  this.name = name || "->son";
}
var s = new Son("son");
console.log(s.name); // son
//s.sayAge(); // 抛出错误
s.sayName(); // son
console.log(s.age); // undefined
console.log(s instanceof Father); // false
console.log(s instanceof Son); // true
  • 优点
    • 解决了原型链继承中子类实例共享父类引用属性的问题
    • 创建子类实例时,可以向父类传递参数
    • 可以实现多继承(call 多个父类对象)
  • 缺点
    • 实例并不是父类实例,只是子类的实例
    • 只能继承父类实例的属性和方法,不能继承其原型上的属性和方法
    • 无法实现函数复用,每个子类都有父类实例的副本,影响性能

3. 实例继承:为父类实例添加新特征,作为子类实例返回

function Son(name) {
  var f = new Father();
  f.name = name || "->son";
  return f;
}
var s = new Son("son");
console.log(s.name); // son
s.sayAge(); // 18
s.sayName(); // son
console.log(s.age); // 18
console.log(s instanceof Father); // true
console.log(s instanceof Son); // false
  • 优点
    • 不限制调用方法,不管是 new 子类()还是子类(),返回的对象具有相同的效果
  • 缺点
    • 实例是父类的实例,不是子类的实例
    • 不支持多继承

4. 组合继承:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Son(name) {
  Father.call(this);
  this.name = name || "->son";
}
Son.prototype = new Father();
// 修复构造函数指向
Son.prototype.constructor = Son;
var s = new Son("son");
console.log(s.name); // son
s.sayAge(); // 18
s.sayName(); // son
console.log(s.age); // 18
console.log(s instanceof Father); // true
console.log(s instanceof Son); // true
  • 优点
    • 弥补了构造继承的缺点,现在既可以继承实例的属性和方法,也可以继承原型的属性和方法
    • 既是子类的实例,也是父类的实例
    • 不存在引用属性共享问题
    • 可传参
    • 函数可以复用
  • 缺点
    • 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

5. 寄生组合继承:通过寄生方式,砍掉父类的实例属性,避免了组合继承生成两份实例的缺点

function Son(name) {
  Father.call(this);
  // var f = new Father();
  this.name = name || "->son";
}
(function () {
  // 创建一个没有实例方法的类
  var None = function () {};
  None.prototype = Father.prototype;
  // 将实例作为子类的原型
  Son.prototype = new None();
  // 修复构造函数指向
  Son.prototype.constructor = Son;
})();
var s = new Son("son");
console.log(s.name); // son
s.sayAge(); // 18
s.sayName(); // son
console.log(s.age); // 18
console.log(s instanceof Father); // true
console.log(s instanceof Son); // true
  • 优点
    • 堪称完美
  • 缺点
    • 实现起来较为复杂

6. Class 继承:使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super

class Son extends Father {
  constructor(name) {
    super(name);
    this.name = name || "->father";
  }
}
var s = new Son("son");
console.log(s.name); // son
s.sayAge(); // 18
s.sayName(); // son
console.log(s.age); // 18
console.log(s instanceof Father); // true
console.log(s instanceof Son); // true

12. new 的实现

  • 创建一个空对象,作为将要返回的实例。
  • 将空对象的原型(proto)指向构造函数的 prototype 属性。
  • 将空对象赋值给构造函数中的 this。
  • 指向构造函数中的代码
function A() {
  this.name = 1;
  this.age = 2;
  // return {} // 如果一个类返回了一个引用空间 那么实例将指向这个空间
}
// let a = new A(); // 1) 创建对象,并且将对象传入到函数中作为this
A.prototype.say = function () {
  console.log("say");
};
function mockNew(A) {
  let obj = {};
  A.call(obj);
  obj.__proto__ = A.prototype;
  return obj;
}
let o = mockNew(A);
o.say();
console.log(o);

13. 大厂面试题

1. promise.all 异常处理(如何做才能做到 promise.all 中即使一个 promise 程序 reject,promise.all 依然能把其他数据正确返回)

Promise.all 异常/出错一般情况下,all 里传 promise 数组,但是只要有一个出错,就会进入到 catch 里,不会捕获到正常数据的

  • 解决方案:
  • 全部改为串行调用(会失去并发优势,不推荐)
  • 使用 catch 捕获异常
const a = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111);
  }, 1000);
});

const b = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(222);
  }, 1000);
});

Promise.all(
  [a, b].map((p) => {
    return p.catch((error) => error);
  })
)
  .then((res) => {
    console.log(res, "res");
  })
  .catch((error) => {
    console.log(error, "error");
  });

2. 版本号比较排序

有一组版本号如下['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']。现在需要对其进行排序,排序的结果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']

const arr = ["0.1.1", "2.3.3", "0.302.1", "4.2", "4.3.5", "4.3.4.5"];

arr.sort((a, b) => {
  const arr1 = a.split(".");
  const arr2 = b.split(".");
  let i = -1;
  while (true) {
    i++;
    const s1 = arr1[i];
    const s2 = arr2[i];
    if ((s1 === undefined) | (s2 === undefined)) {
      return arr2.length - arr1.length;
    }
    if (s1 === s2) continue;
    return s2 - s1;
  }
});

console.log(arr);

3. 数组中第 k 个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素

  • 示例 1:输入: [3,2,1,5,6,4] 和 k = 2,输出: 5

  • 示例 2: 输入: [3,2,3,1,2,4,5,5,6] 和 k = 4,输出: 4

    如果用快速排序算法(先排序),排序时间复杂度是 O(NlogN),其中 N 表示数组 nums 的长度,想要第 k 大的元素,却给整个数组排序,有点杀鸡用牛刀的感觉,所以这里就有一些小技巧了,可以把时间复杂度降低到 O(NlogK)甚至是 O(N)

4. 二叉树中的所有路径

5. 二叉树中和为某一值的路径

6. node 中 promisify 实现

util 核心模块 中将方法 promise 化 ,以实现 async + await + promise

  • 使用示例
const http = require("http");
const url = require("url");
const fs = require('fs');
const { promisify } = require("util");
const readFile = promisify(fs.readFile);

http.createServer((req, res) => {
    (async () => {
        try {
            let data = await readFile('./index2.html');
            res.statusCode = 200;
            res.setHeader('Content-Type', "text/html;charset=utf-8");
            res.end(data);
        } catch (err) {
            console.log(err);
        }
    })();
  • 实现原理如下
// 实现 promisify 的原理
const promisify1 =
  (fn) =>
  (...args) => {
    return new Promise((resolve, reject) => {
      fn(...args, function (err) {
        // node 异步的特点,最后一个参数是回调函数 只针对node 因为node是这种error-first的形式
        if (err) reject(err);
        resolve();
      });
    });
  };

7. fetch 兼容超时重传

实现 fetch 请求超时/错误重新发送一次

  • 阻断请求方法:
    • AbortController 控制器,可以阻断 web 请求(不兼容 IE)
    // IE不支持
    const controller = new AbortController();
    // AbortController.signal属性获取其关联 AbortSignal对象的引用
    const signal = controller.signal;
    controller.abort();
    
    • 兼容 ie 的 XMLHttpRequest.abort()
  • 超时重发 => Promise.race: 同时发送多个请求,返回最先请求到的结果
let newFetch = (url) => {
  // 重传次数控制
  let requestAgain = 0;
  // 创建控制器对象
  const controller = new AbortController();
  // AbortController.signal属性获取其关联 AbortSignal对象的引用
  const signal = controller.signal;
  //判断是否为IE 该判断不支持IE11 以上
  if (navigator.userAgent.indexOf("MSIE") > -1) {
    // 如果是ie 不支持-- AbortController--
    // 也不支持fetch
    // 此时使用原生xmlHttpRequest
    // ie5/6和ie6+创建xmlHttpRequest对象有所不同
    return new Promise((resolve, reject) => {
      xmlreq(url, "GET", resolve);
    })
      .then((res) => {
        // console.log('success')
        // console.log(res)
      })
      .catch((err) => {
        // 重新发送请求一次
        if (requestAgain != 0) return;
        requestAgain++;
        xmlreq(url, "GET", resolve);
      });
  } else {
    // 不是ie
    return Promise.race([
      requestPromise(url, signal)
        .then(async (res) => {
          let result = await res.json();
          // console.log(result)
        })
        .catch((err) => {
          // 错误时重发
          // 使用控制器终止请求
          controller.abort();
          // 重新发送请求
          requestPromise(url).then(async (res) => {
            let result = await res.json();
            // console.log('请求重发')
            // console.log(result)
          });
        }),
      new Promise((resolve, reject) => {
        setTimeout(() => {
          // 使用控制器终止请求
          controller.abort();
          // 重新发送请求一次
          if (requestAgain != 0) return;
          requestAgain++;
          requestPromise(url).then(async (res) => {
            let result = await res.json();
            // console.log('请求重发')
            // console.log(result)
          });
        }, 30000);
      }),
    ]);
  }
};
// 封装fetch 请求
let requestPromise = (url, signal = {}) => {
  return fetch(url, {
    signal,
    headers: {
      "Content-Type": "application/json;charset=utf-8;",
    },
  });
};
// 封装xmlHTTPRequest
let xmlreq = (url, methods = "GET", resolve) => {
  // 创建xmlHttpRequest 对象
  let xmlHttpReq;
  if (window.XMLHttpRequest) {
    // ie6+
    xmlHttpReq = new XMLHttpRequest();
  } else {
    // ie5/6
    xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
  }
  // 设置超时时间
  xmlHttpReq.timeout = 30000;
  xmlHttpReq.open("GET", url);
  xmlHttpReq.send();
  xmlHttpReq.ontimeout = (event) => {
    // 终止上一次请求
    xmlHttpReq.abort();
    // 重新发送请求
    xmlHttpReq.send();
  };
  xmlHttpReq.onreadystatechange = function (event) {
    if (event.target.readyState == 4 && event.target.status == 200) {
      let resData = JSON.parse(xmlHttpReq.response);
      resolve(resData);
    }
  };
};
newFetch("http://a.com");

8. 观察者模式(高频)

当一个变量值被修改时,可以自动通知所有关注这个变量的其他对象,自动重新更新获取这个变量的新值

  • 观察者模式是由具体的目标调用,例如:当事件触发 Dep 就会去调用观察者的方法,所以订阅者和观察者之间是存在依赖的。
  • 发布订阅模式是由统一的调度中心(eventBus)调用,因为发布者和订阅者不需要知道对方的存在。
// 发布者-目标
class Dep {
  constructor() {
    // 记录所有的订阅者
    this.subs = [];
  }
  // 添加订阅者
  addSub(sub) {
    // 判断订阅者是否存在,并且有update 方法
    if (sub && sub.update) {
      this.subs.push(sub);
    }
  }
  // 发布通知
  notify() {
    // 循环发布通知
    this.subs.forEach((s) => {
      s.update();
    });
  }
}

// 订阅者-观察者
class Watcher {
  update() {
    console.log("update --->");
  }
}

let dep = new Dep();
let watcher = new Watcher();
// 添加订阅者
dep.addSub(watcher);
// 发布执行
dep.notify();

// 执行结果 update --->

9. String indexOf 实现

10. 扁平化

将一个多维数组变为一维数组

  • reduce 实现
function flatten(arr) {
  return arr.reduce((result, item) => {
    return result.concat(Array.isArray(item) ? flatten(item) : item);
  }, []);
}
  • ... 拓展运算符实现
function flatten(arr) {
  while (arr.some((item) => Array.isArray(item))) {
    arr = [].concat(...arr);
  }
  return arr;
}

11. 函数柯里化

函数柯里化 把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术

const curry = (fn, args = []) => {
  const len = fn.length;
  return (...rest) => {
    const arr1 = args.concat(rest);
    if (arr1.length < len) {
      return curry(fn, arr1);
    }
    return fn(...arr1);
  };
};

function add(a, b, c, d, e, f) {
  var val = a + b + c + d + e + f;
  return val;
}

// let sum = curry(add)(1)(2, 3)(4, 5, 6);
let sum = curry(add)(1)(2)(3)(4)(5, 6);

console.log(sum);
  • 如题: 实现一个 add 方法,使计算结果能够满足如下预期:
    • add(1)(2)(3) = 6;
    • add(1, 2, 3)(4) = 10;
    • add(1)(2)(3)(4,5) = 15;
function add() {
  // 第一次执行时,定义一个数组专门用来存储所有的参数
  var _args = Array.prototype.slice.call(arguments);

  // 在内部声明一个函数,利用闭包的特性保存_args并且收集所有的参数值
  var _adder = function () {
    _args.push(...arguments);
    return _adder;
  };

  // 利用toString的隐式转换的特性
  // 当最后执行的时候进行隐式转换,并进行最终的值返回
  _adder.toString = function () {
    return _args.reduce(function (a, b) {
      return a + b;
    });
  };
  return _adder;
}

// 6
console.log(add(1)(2)(3));
// 10
console.log(add(1, 2, 3)(4));
// 15
console.log(add(1)(2)(3)(4)(5));

14. vue 如何实现组件封装

  • Vue.extend 创建组件的构造函数
  • Vue.component 注册全局组件
  • 在实例化 extends 组件构造器时,传入属性必须是 propsData、而不是 props

15. vue 内导航守卫 以及 路由独享守卫、组件内守卫

全局导航守卫

  1. router.beforeEach 全局前置守卫
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})
  1. router.beforeResolve 全局解析守卫

    • 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
  2. router.afterEach 全局后置钩子

  • 可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身

路由独享的守卫

  • 可以在路由配置上直接定义 beforeEnter 守卫 , 与全局前置守卫的方法参数是一样的
const router = new VueRouter({
  routes: [
    {
      path: "/foo",
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      },
    },
  ],
});

组件内的守卫

  • beforeRouteEnter
  • beforeRouteUpdate
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  },
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • 一、CSS问题 1.flex布局 display:flex; 在父元素设置,子元素受弹性盒影响,默认排成一行,如果...
    陈二狗想吃肉阅读 566评论 0 9
  • 常见前端面试题 第一天 ✨ 1. HTML5有哪些新特性?如何处理HTML5新标签的浏览器兼容问题?如何区分HTM...
    小曼很nice阅读 1,130评论 2 12
  • 前言 面试题是永远都准备不完的!!!!! 前端常见的一些问题 1.前端性能优化手段? 1. 尽可能使用雪碧图2. ...
    ZJ懒得写简书阅读 10,517评论 22 266
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 124,811评论 2 7
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,044评论 0 4