用Class寫一個記住用戶離開位置的js插件

前言

常見的js插件都很少使用ES6的class,一般都是通過構(gòu)造函數(shù),而且常常是手寫CMD斑唬、AMD規(guī)范來封裝一個庫,比如這樣:

// 引用自:http://www.reibang.com/p/e65c246beac1
;(function(undefined) {
    "use strict"
    var _global;
    var plugin = {
      // ...
    }
    _global = (function(){ return this || (0, eval)('this'); }());
    if (typeof module !== "undefined" && module.exports) {
        module.exports = plugin;
    } else if (typeof define === "function" && define.amd) {
        define(function(){return plugin;});
    } else {
        !('plugin' in _global) && (_global.plugin = plugin);
    }
}());

但現(xiàn)在都9102年了黎泣,是時候祭出我們的ES6大法了恕刘,可以用更優(yōu)雅的的寫法來實現(xiàn)一個庫,比如這樣:

class RememberScroll {
    constructor(options) {
        ...
    }
}
export default RememberScroll

在這篇文章抒倚,博主主要通過分享最近自己寫的一個記住頁面滾動位置小插件褐着,講一下如何用class語法配合webpack 4.xbabel 7.x封裝一個可用的庫。

項目地址:Github托呕, 在線Demo:Demo

喜歡的朋友希望能點個Star收藏一下含蓉,非常感謝。

需求來源

相信很多同學都會遇到這樣一個需求:用戶瀏覽一個頁面并離開后项郊,再次打開時需要重新定位到上一次離開的位置馅扣。

這個需求很常見,我們平時在手機上閱讀微信公眾號的文章頁面就有這個功能着降。想要做到這個需求差油,也比較好實現(xiàn),但博主有點懶,心想有沒有現(xiàn)成的庫可以直接用呢蓄喇?于是去GitHub上搜了一波发侵,發(fā)現(xiàn)并沒有很好的且符合我需求的,于是得自己實現(xiàn)一下妆偏。

為了靈活使用(只是部分頁面需要這個功能)刃鳄,博主在項目中單獨封裝了這個庫,本來是在公司項目中用的钱骂,后來想想何不開源出來呢叔锐?于是有了這個分享,這也是對自己工作的一個總結(jié)见秽。

預期效果

博主喜歡在做一件事情前先yy一下預期的效果掌腰。博主希望這個庫用起來盡量簡單,最好是插入一句代碼就可以了张吉,比如這樣:

<html>
<head>
  <meta charset="utf-8">
  <title>remember-scroll examples</title>
</head>
<body>
  <div id="content"></div>
  <script src="../dist/remember-scroll.js"></script>
  <script>
    new RememberScroll()
  </script>
</body>
</html>

在想要加上記住用戶瀏覽位置的頁面上引入一下庫,然后new RememberScroll()初始化一下即可催植。

下面就帶著這個目標肮蛹,一步一步去實現(xiàn)啦。

設計方案

1. 需要存哪些信息创南?

用戶瀏覽頁面的位置伦忠,主要需要存兩個字段:哪個頁面離開時的位置,通過這兩個字段稿辙,我們才可以在用戶第二次打開網(wǎng)站的頁面時昆码,命中該頁面,并自動跳轉(zhuǎn)到上一次離開的位置邻储。

2.存在哪赋咽?

記住瀏覽位置,需要將用戶離開前的瀏覽位置記錄在客戶端的瀏覽器中吨娜。這些信息可以主要存放在:cookie脓匿、sessionStoragelocalStorage中宦赠。

  1. 存放在cookie陪毡,大小4K,空間雖有限但也勉強可以勾扭。但cookie是每次請求服務器時都會攜帶上的毡琉,無形中增加了帶寬和服務器壓力,所以總體來說是不太合適的妙色。
  2. 存放在sessionStorage中桅滋,由于僅在當前會話下有效,用戶離開頁面sessionStorage就會被清除身辨,所以不能滿足我們的需求虱歪。
  3. 存放在localStorage,瀏覽器可永久保存蜂绎,大小一般限制5M,滿足我們需求笋鄙。

綜上师枣,最后我們應該選擇localStorage

3. 需注意的問題

  1. 一個站點可能有很多頁面萧落,如何標識是哪個頁面呢践美?

一般來說可以用頁面的url作為頁面的唯一標識,比如:www.xx.com/article/${id},不同的id對應不同的頁面找岖。

但博主考慮到現(xiàn)在很多站點都是用spa了陨倡,而且常見在url后面會帶有#xxx的哈希值,如www.xx.com/article/${id}#tag1www.xx.com/article/${id}#tag2這種情況许布,這可能表示的是同一個頁面的不同錨點兴革,所以用url作為頁面的唯一標識不太可靠。

因此蜜唾,博主決定將這個頁面唯一標識作為一個參數(shù)來讓使用者來決定杂曲,姑且命名為pageKey,讓使用者保證是全站唯一的即可袁余。

  1. 如果用戶訪問我們的站點中很多很多的頁面擎勘,由于localStorage是永久保存的,如何避免localStorage不斷累積占用過大颖榜?

我們的需求可能僅僅是想近期記住即可棚饵,即只需要記住用戶的瀏覽位置幾天,可能會更希望我們存的數(shù)據(jù)能夠自動過期掩完。

localStorage自身是沒有自動過期機制的噪漾,一般只能在存數(shù)據(jù)的時候同時存一下時間戳,然后在使用時判斷是否過期且蓬。如果只能是在使用時才判斷是否清除怪与,而新訪問頁面時又會生成新的記錄,localStorage中始終都會存在至少一條記錄的缅疟,也就是說無法真正實現(xiàn)自動過期分别。這里不禁就覺得有點多余了,既然都是會一直保留記錄在localStorage中存淫,那干脆就不判斷了耘斩,咱換一個思路:只記錄有限的最新頁面數(shù)量

舉個例子:

咱們網(wǎng)站有個文章頁:www.xx.com/articles/${id}桅咆,每個的id表示不同的文章括授,咱們只記錄用戶最新訪問的5篇文章,即維護一個長度為5的隊列。

比如當前網(wǎng)站有id從1100篇文章荚虚,用戶分別訪問第1,2,3,4,5篇文章時薛夜,這5篇文章都會記錄離開的位置,而當用戶打開第六篇文章時版述,第六條記錄入隊的同時第一條記錄出隊梯澜,此時localStorage中記錄的是2,3,4,5,6這幾篇文章的位置,這就保證了localStorage永遠不會累積存儲數(shù)據(jù)且舊記錄會隨著不斷訪問新頁面自動“過期”渴析。

為了更靈活一點晚伙,博主決定給這個插件添加一個maxLength的參數(shù),表示當前站點下記錄的最新的頁面最大數(shù)量俭茧,默認值設為5咆疗,如果有小伙伴的需求是記錄更多的頁面,可以通過這個參數(shù)來設置母债。

4. 實現(xiàn)思路

  1. 我們需要時刻監(jiān)聽用戶瀏覽頁面時的滾動條的位置午磁,可以通過window.onscroll事件,獲得當前的滾動條位置:scrollTop毡们。
  2. scrollTop和頁面唯一標識pageKey存進localStorage中迅皇。
  3. 用戶再次打開之前訪問過的頁面,在頁面初始化時漏隐,讀取localStorage中的數(shù)據(jù),判斷頁面的pageKey是否一致奴迅,若一致則將頁面的滾動條位置自動滾動到相應的scrollTop值青责。

是不是很簡單?不過實現(xiàn)的過程中需要注意一下細節(jié)取具,比如做一下防抖處理脖隶。

實現(xiàn)步驟

逼逼了這么久,是時候開始擼代碼了暇检。

1.封裝localStorage工具方法

工欲善其事产阱,必先利其器。為更好服務接下來的工作块仆,咱們先簡單封裝一下調(diào)用localStorage的幾個方法,主要是get,set,remove

// storage.js
const Storage = {
  isSupport () {
    if (window.localStorage) {
      return true
    } else {
      console.error('Your browser cannot support localStorage!')
      return false
    }
  },
  get (key) {
    if (!this.isSupport) {
      return
    }
    const data = window.localStorage.getItem(key)
    return data ? JSON.parse(data) : undefined
  },
  remove (key) {
    if (!this.isSupport) {
      return
    }
    window.localStorage.removeItem(key)
  },
  set (key, data) {
    if (!this.isSupport) {
      return
    }
    const newData = JSON.stringify(data)
    window.localStorage.setItem(key, newData)
  }
}

export default Storage

2. class大法

class即類构蹬,本質(zhì)上雖然是一個function,但使用class定義一個類會更直觀。咱們?yōu)榧磳懙膸炱饌€名字為RememberScroll悔据,開始就是如下的樣子啦:

import Storage from './storage'
class RememberScroll {
    constructor() {
        
    }
}

1.處理傳進來的參數(shù)

我們需要在類的構(gòu)造函數(shù)constructor中接收參數(shù)庄敛,并覆蓋默認參數(shù)。

還記得上面咱們預期的用法嗎科汗?即new RememberScroll({pageKey: 'myPage', maxLength: 10})藻烤。

  constructor (options) {
    let defaultOptions = {
      pageKey: '_page1', // 當前頁面的唯一標識
      maxLength: 5
    }
    this.options = Object.assign({}, defaultOptions, options)
}

如果沒有傳參數(shù),就會使用默認的參數(shù),如果傳了參數(shù)怖亭,就使用傳進來的參數(shù)涎显。this.options就是最終處理后的參數(shù)啦。

2.頁面初始化

當頁面初始化時兴猩,咱們需要做三件事情:

  • loaclStorage取出緩存列表
  • 將滾動條滾動到記錄的位置(若有記錄的話)期吓;
  • 注冊window.onscroll事件監(jiān)聽用戶滾動行為;
    因此峭跳,需要在構(gòu)造函數(shù)中就執(zhí)行initScrolladdScrollEvent這兩個方法:
import Storage from './utils/storage'
class RememberScroll {
  constructor (options) {
    // ...
    this.storageKey = '_rememberScroll'
    this.list = Storage.get(this.storageKey) || []
    this.initScroll()
    this.addScrollEvent()
  }
  initScroll () {
    // ...
  }
  addScrollEvent () {
    // ...
  }
}

這里咱們將localStorage中的鍵名命名為_rememberScroll膘婶,應該能夠盡量避免和平常站點使用localStorage的鍵名沖突。

3.監(jiān)聽滾動事件:addScrollEvent()的實現(xiàn)

  addScrollEvent () {
    window.onscroll = () => {
      // 獲取最新的位置蛀醉,只記錄垂直方向的位置
      const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
      // 構(gòu)造當前頁面的數(shù)據(jù)對象
      const data = {
        pageKey: this.options.pageKey,
        y: scrollTop
      }
      let index = this.list.findIndex(item => item.pageKey === data.pageKey)
      if (index >= 0) {
        // 之前緩存過該頁面悬襟,則替換掉之前的記錄
        this.list.splice(index, 1, data)
      } else {
        // 如果已經(jīng)超出長度了,則清除一條最早的記錄
        if (this.list.length >= this.options.maxLength) {
          this.list.shift()
        }
        this.list.push(data)
      }
      // 更新localStorage里面的記錄
      Storage.set(this.storageKey, this.list)
    }
  }

ps:這里最好需要做一下防抖處理

4.初始化滾動條位置: initScroll()的實現(xiàn)

initScroll () {
    // 先判斷是否有記錄
    if (this.list.length) {
      // 當前頁面pageKey是否一致
      let currentPage = this.list.find(item => item.pageKey === this.options.pageKey)
      if (currentPage) {
        setTimeout(() => {
          // 一致拯刁,則滾動到對應的y值
          window.scrollTo(0, currentPage.y)
        }, 0)
    }
}

細心的同學可能會發(fā)現(xiàn)脊岳,這里用了setTimeout,而不是直接調(diào)用window.scrollTo。這是因為博主在這里遇到坑了垛玻,這里涉及到頁面加載執(zhí)行順序的問題割捅。

在執(zhí)行window.scrollTo前,頁面必須是已經(jīng)加載完成了的帚桩,滾動條要已存在才可以滾動對吧亿驾。如果頁面加載時直接執(zhí)行,當時的scroll高度可能為0账嚎,window.scrollTo執(zhí)行就會無效莫瞬。如果頁面的數(shù)據(jù)是異步獲取的,也會導致window.scrollTo無效郭蕉。因此用setTimeout會是比較穩(wěn)的一個辦法疼邀。

5.將模塊export出去

最后我們需要將模塊export出去,整體代碼大概是這個樣子:

import Storage from './utils/storage'

class RememberScroll {
  constructor (options) {
    let defaultOptions = {
      pageKey: '_page1', // 當前頁面的唯一標識
      maxLength: 5
    }
    this.storageKey = '_rememberScroll'
    // 參數(shù)
    this.options = Object.assign({}, defaultOptions, options)

    // 緩存列表
    this.list = Storage.get(this.storageKey) || []
    this.initScroll()
    this.addScrollEvent()
  }
  initScroll () {
    // ...
  }
  addScrollEvent () {
    // ...
  }
}

export default RememberScroll

這樣就基本完成整個插件的功能啦召锈,是不是很簡單哈哈旁振。篇幅原因就不貼具體代碼了,可以直接到GitHub上看:remember-scroll

打包

接下來應該是本文的重點了涨岁,首先要清楚為什么要打包拐袜?

  1. 將項目中所用到的js文件合并,只對外輸出一個js文件梢薪。
  2. 使項目同時支持AMD,CMD阻肿、瀏覽器<script>標簽引入,即umd規(guī)范沮尿。
  3. 配合babel丛塌,將es6語法轉(zhuǎn)為es5語法较解,兼容低版本瀏覽器。

PS: 由于webpack和babel更新速度很快赴邻,網(wǎng)上很多教程可能早已過時印衔,現(xiàn)在(2019-03)的版本已經(jīng)是babel 7.3.0,webpack 4.29.6, 本篇文章只分享現(xiàn)在的最新的配置方法姥敛,因此本篇文章也是會過時的奸焙,讀者們請注意版本號。

npm init項目

咱們先新建一個目錄彤敛,這里名為:remember-scroll与帆,然后將上面寫好的remember-scroll.js放進remember-scroll/src/目錄下。

PS:一般項目的資源文件都放在src目錄下墨榄,為了顯得專業(yè)點玄糟,最好將remember-scroll.js改名為index.js。)

此時項目還沒有package.json文件袄秩,因此在根目錄執(zhí)行命令初始化package.json:

npm init

需要根據(jù)提示填寫一些項目相關信息阵翎。

安裝webpack和webpack-cli

運行webpack命令時需要同時裝上webpack-cli

npm i webpack webpack-cli -D

配置webpack.config.js

在根目錄中添加一個webpack.config.js,按照webpack官網(wǎng)的示例代碼配置:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'remember-scroll.js' // 修改下輸出的名稱
  }
};

然后在package.json的script中配置運行webpack的命令:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack --mode=development --colors"
  },

這樣配置完成,在根目錄運行npm run dev之剧,會自動生成dist/remember-scroll.js郭卫。

此時已經(jīng)實現(xiàn)了我們的第一個小目標:賺它一個億,哦不背稼,是將storage.jsindex.js合并輸出為一個remember-scroll.js贰军。

這種簡單的打包可以稱為:非模塊化打包。由于我們在js文件中沒有通過AMD的return或者CommonJS的exports或者this導出模塊本身蟹肘,導致模塊被引入的時候只能執(zhí)行代碼而無法將模塊引入后賦值給其它模塊使用词疼。

支持umd規(guī)范

相信很多同學都聽過AMD,CommonJS規(guī)范了,不清楚的同學可以看看阮一峰老師的介紹:Javascript模塊化編程(二):AMD規(guī)范疆前。

為了讓我們的插件同時支持AMD,CommonJS,所以需要將我們的插件打包為umd通用模塊寒跳。

之前看過一篇文章:如何定義一個高逼格的原生JS插件聘萨,在沒有使用webpack打包時竹椒,需要在插件中手寫支持這些模塊化的代碼:

// 引用自:http://www.reibang.com/p/e65c246beac1
;(function(undefined) {
    "use strict"
    var _global;
    var plugin = {
      // ...
    }
    // 最后將插件對象暴露給全局對象
    _global = (function(){ return this || (0, eval)('this'); }());
    if (typeof module !== "undefined" && module.exports) {
        module.exports = plugin;
    } else if (typeof define === "function" && define.amd) {
        define(function(){return plugin;});
    } else {
        !('plugin' in _global) && (_global.plugin = plugin);
    }
}());

博主看到這坨東西,也是有點暈米辐,不得不佩服大佬就是大佬胸完。還好現(xiàn)在有了webpack,我們現(xiàn)在只需要寫好主體關鍵代碼翘贮,webpack會幫我們處理好這些打包的問題赊窥。

在webpack4中,我們可以將js打包為一個庫的形式狸页,詳情可看:Webpack Expose the Library
锨能。在我們這里只需在output中加上library屬性:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'remember-scroll.js',
    library: 'RememberScroll',
    libraryTarget: 'umd',
    libraryExport: 'default'
  }
};

注意libraryTargetumd扯再,就是我們要打包的目標規(guī)范為umd

當我們在html中通過script標簽引入這個js時址遇,會在window下注冊RememberScroll這個變量(類似引入jQuery時會在全局注冊$這個變量)熄阻。此時就直接使用RememberScroll這個變量了。

<script src="../dist/remember-scroll.js"></script>
<script>
  console.log(RememberScroll)
</script>

這里有個坑需要注意一下倔约,如果沒有加上libraryExport: 'default',由于我們代碼中是export default RememberScroll,打包出來的代碼會類似:

{
    'default': {
        initScroll () {}
    }
}

而我們期望的是這樣:

{
    initScroll () {}
}

即我們希望的是直接輸出default中的內(nèi)容秃殉,而不是隔著一層default。所以這里還要加上libraryExport: 'default'浸剩,打包時只輸出default的內(nèi)容钾军。

PS: webpack英文文檔看得有點懵逼,這個坑讓博主折騰了很久才爬起來绢要,所以特別講下吏恭。剛興趣的同學可以看下文檔:output.libraryExport

到這里袖扛,已經(jīng)實現(xiàn)了我們的第二個小目標:支持umd規(guī)范砸泛。

使用babel-loader

上面我們打包出來的js,其實已經(jīng)可以正常運行在支持es6語法的瀏覽器中了蛆封,比如chrome唇礁。但想要運行在IE10,IE11中惨篱,還得讓神器Babel幫我們一把盏筐。

PS: 雖然很多人說不考慮兼容IE了,但作為一個通用性的庫砸讳,古董級的IE7,8,9可以不兼容琢融,但較新版本的IE10,11還是需要兼容一下的。

Babel是一個JavaScript轉(zhuǎn)譯器簿寂,相信大家都聽過漾抬。由于JavaScript在不斷的發(fā)展,但是瀏覽器的發(fā)展速度跟不上常遂,新的語法和特性不能馬上被瀏覽器支持纳令,因此需要一個能將新語法新特性轉(zhuǎn)為現(xiàn)代瀏覽器能理解的語法的轉(zhuǎn)譯器,而Babel就是充當了轉(zhuǎn)譯器的角色克胳。

PS:以前博主一直以為(相信很多剛接觸Babel的同學也是這樣)平绩,只要使用了Babel,就可以放心無痛使用ES6的語法了漠另,然而事情并不是這樣捏雌。Babel編譯并不會做polyfill,Babel為了保證正確的語義笆搓,只能轉(zhuǎn)換語法而不會增加或修改原有的屬性和方法性湿。要想無痛使用ES6纬傲,還需要配合polyfill。不太理解的同學肤频,在這里推薦大家看下這篇文章:21 分鐘精通前端 Polyfill 方案嘹锁,寫得非常通俗易懂。

總的來說着裹,就是Babel需要配合polyfill來使用领猾。

Babel更新比較頻繁,網(wǎng)上搜出來的很多配置教程是舊版本的骇扇,可能并不適用最新的Babel 7.x摔竿,所以我們這里折騰一下最新的webpack4配置Babel方案:babel-loader
1.安裝babel-loader,@babel/core少孝、@babel/preset-env继低。

npm install -D babel-loader @babel/core @babel/preset-env core-js

core-js是JavaScript模塊化標準庫,在@babel/preset-env按需打包時會使用core-js中的函數(shù)稍走,因此這里也是要安裝的袁翁,不然打包的時候會報錯。

2.修改webpack.config.js配置婿脸,添加rules

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'remember-scroll.js',
    library: 'RememberScroll',
    libraryTarget: 'umd',
    libraryExport: 'default'
  },
  module: {
    rules: [
        {
          test: /\.m?js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: 'babel-loader'
          }
        }
      ]
  }
};

表示.js的代碼使用babel-loader打包粱胜。

3.在根目錄新建babel.config.js,參考Babel官網(wǎng)

const presets = [
  [
    "@babel/env",
    {
      targets: {
        browsers: [
            "last 1 version",
            "> 1%",
            "maintained node versions",
            "not dead"
          ]
      },
      useBuiltIns: "usage",
    },
  ],
];

browsers配置的是目標瀏覽器狐树,即我們想要兼容到哪些瀏覽器焙压,比如我們想兼容到IE10,就可以寫上IE10抑钟,然后webpack會在打包時自動為我們的庫添加polyfill兼容到IE10芋酌。

博主這里用的是推薦的參數(shù)艺玲,來自:npm browserslist抑淫,這樣就能兼容到大多數(shù)瀏覽器啦会前。

配置好后,npm run dev打包即可蛔溃。
此時绰沥,我們已經(jīng)實現(xiàn)了第三個小目標:兼容低版本瀏覽器。

生產(chǎn)環(huán)境打包

npm run dev打包出來的js會比較大城榛,一般還需要壓縮一下揪利,而我們可以使用webpack的production模式态兴,就會自動為我們壓縮js狠持,輸出一個生產(chǎn)環(huán)境可用的包。在package.json再添加一條build命令:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --mode=production -o dist/remember-scroll.min.js --colors",
    "dev": "webpack --mode=development --colors"
  },

這里同時指定了輸出的文件名為:remember-scroll.min.js瞻润,一般生產(chǎn)環(huán)境就是使用這個文件啦喘垂。

發(fā)布到npm

經(jīng)過上面的步驟甜刻,我們已經(jīng)寫完這個庫,有需求的同學可以將庫發(fā)布到npm正勒,讓更多的人可以方便用到你這個庫得院。

在發(fā)布到npm前,需要修改一下package.json章贞,完善下描述作者之類的信息祥绞,最重要的是要添加main入口文件:

{
    "main": "dist/remember-scroll.min.js",
}

這樣別人使用你的庫時,可以直接通過import RememberScroll from 'remember-scroll'來使用remember-scroll.min.js鸭限。

發(fā)布步驟:

  1. 先到https://www.npmjs.com/注冊一個賬號蜕径,然后驗證郵箱。
  2. 然后在命令行中輸入:npm adduser败京,輸入賬號密碼郵箱登錄兜喻。
  3. 運行npm publish上傳包,幾分鐘后就可以在npm搜到你的包了赡麦。

至此朴皆,基本就完成一個插件的開發(fā)發(fā)布過程啦。

不過一個優(yōu)秀的開源項目泛粹,還應該要有詳細的說明文檔遂铡,使用示例等等,大家可以參考下博主這個項目的README.md晶姊, 中文README.md忧便。

最后

文章寫了好幾天了,可謂嘔心瀝血帽借,雖然比較啰嗦珠增,但應該比較清楚地交代了如何運用ES6語法從零寫一個記住用戶離開位置的js插件,也很詳細地講解了如何用最新的webpack打包我們的庫砍艾,希望能讓大家都有所收獲,也希望大家能到GitHub上點個Star鼓勵一下啦蒂教。

remember-scroll這個插件其實幾個月前就已經(jīng)發(fā)布到npm了,一直比較忙(懶)沒寫章分享脆荷。雖然功能簡單但很有誠意凝垛,能兼容到IE9。

使用起來也非常方便簡單蜓谋,可直接通過script標簽cdn引入梦皮,也可以在vue中import RememberScroll from 'remember-scroll'使用。文檔中有詳細的使用示例:

項目地址Github桃焕,在線Demo剑肯。

歡迎大家評論交流,也歡迎PR观堂,同時希望大家能點個Star鼓勵一下啦让网。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末呀忧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子溃睹,更是在濱河造成了極大的恐慌而账,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件因篇,死亡現(xiàn)場離奇詭異泞辐,居然都是意外死亡,警方通過查閱死者的電腦和手機竞滓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門铛碑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人虽界,你說我怎么就攤上這事汽烦。” “怎么了莉御?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵撇吞,是天一觀的道長。 經(jīng)常有香客問我礁叔,道長牍颈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任琅关,我火速辦了婚禮煮岁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涣易。我一直安慰自己画机,他們只是感情好,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布新症。 她就那樣靜靜地躺著步氏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪徒爹。 梳的紋絲不亂的頭發(fā)上荚醒,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機與錄音隆嗅,去河邊找鬼界阁。 笑死,一個胖子當著我的面吹牛胖喳,可吹牛的內(nèi)容都是我干的泡躯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼精续!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起粹懒,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤重付,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后凫乖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體确垫,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年帽芽,在試婚紗的時候發(fā)現(xiàn)自己被綠了删掀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡导街,死狀恐怖披泪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搬瑰,我是刑警寧澤款票,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站泽论,受9級特大地震影響艾少,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜翼悴,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一缚够、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鹦赎,春花似錦谍椅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至煞额,卻和暖如春思恐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膊毁。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工胀莹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人婚温。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓描焰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子荆秦,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 33篱竭、JS中的本地存儲 把一些信息存儲在當前瀏覽器指定域下的某一個地方(存儲到物理硬盤中)1、不能跨瀏覽器傳輸:在...
    萌妹撒閱讀 2,085評論 0 2
  • 寫在開頭 先說說為什么要寫這篇文章, 最初的原因是組里的小朋友們看了webpack文檔后, 表情都是這樣的: (摘...
    Lefter閱讀 5,296評論 4 31
  • 作者:小 boy (滬江前端開發(fā)工程師)本文原創(chuàng),轉(zhuǎn)載請注明作者及出處瓤介。原文地址:https://www.smas...
    iKcamp閱讀 2,764評論 0 18
  • 1. 新建一個文件夾,命名為 webpack-cli , webpack-cli 就是你的項目名,項目名建議使用小...
    魯大師666閱讀 1,481評論 1 3
  • 時間宛如逝去的陽光吕喘,被風輕輕的帶走,四十五分鐘就在老師的粉筆聲和鳥鳴聲中帶走 “鈴……”下課鈴響起刑桑,老師的板書聲漸...
    清幽浮夢閱讀 180評論 0 0