一文搞懂webpack打包優(yōu)化方案

webpackhot.jpg

寫在前面

在現(xiàn)在前端工程化的大背景下急波,webpack成為了最常用的打包工具之一鸯隅,有一社區(qū)或者優(yōu)秀團(tuán)隊(duì)巨朦,也都以Webpack為基礎(chǔ)構(gòu)建自己的腳手架,比如我們所熟知對(duì)的vue-cli赶撰,umijs等舌镶,通常情況下,這些腳手架多多少少會(huì)為我們配置好一些關(guān)于打包優(yōu)化的東西扣囊,如果你的項(xiàng)目并不復(fù)雜乎折,可能很長一段時(shí)間你都無法感知打包優(yōu)化的重要性绒疗,如果忽然遇到了打包優(yōu)化的問題侵歇,可能太過讓人措手不及,不管你使用的是社區(qū)優(yōu)秀的腳手架吓蘑,還是自己基于webpack搭建的項(xiàng)目或者腳手架惕虑,搞懂webpack打包優(yōu)化坟冲,會(huì)讓我擁有解決更多高級(jí)問題的能力,也會(huì)讓你的項(xiàng)目更加“絲滑”溃蔫。

webpack打包優(yōu)化

打包優(yōu)化主要從兩個(gè)方面下手

  • 打包速度健提,優(yōu)化打包速度,主要是提升了我們的開發(fā)效率伟叛,更快的打包構(gòu)建過程私痹,將讓你保持一顆愉悅的心
  • 打包大小,優(yōu)化打包體積统刮,主要是提升產(chǎn)品的使用體驗(yàn)紊遵,降低服務(wù)器資源成本,更快的頁面加載侥蒙,將讓產(chǎn)品顯得更加“絲滑”暗膜,同時(shí)也可以讓打包更快
打包速度優(yōu)化

當(dāng)我們做一些較大型項(xiàng)目的打包時(shí),經(jīng)常會(huì)遇到鞭衩,打包時(shí)間過長的
問題学搜,讓人焦急不已,那么我們就要采用一些手段來提升webpack的打包论衍。

跟上技術(shù)的迭代(webapck,Node, Npm)

如果想要提升打包的速度瑞佩,將打包技術(shù)生態(tài)中涉及的技術(shù)版本更新將是一個(gè)最簡單的方式,那么為什么更新版本會(huì)提升打包速度呢饲齐?
Webpack的每次更新钉凌,必然會(huì)更新底層的一些打包原理和api來提升打包速度,更新Webpack版本將有助于提升打包速度捂人,同事御雕,webpack又是運(yùn)行在Node環(huán)境下,如果Node版本提升滥搭,其運(yùn)行效率也會(huì)提升酸纲,那么webpack運(yùn)行在node之上也會(huì)有所提升的,同樣瑟匆,我們使用更新的Npm或者Yarn的包管理工具的話闽坡,新的包管理工具會(huì)更快的幫我們分析一下包的依賴或者包的引入,這樣也會(huì)間接的提升webpack的打包速度愁溜。

在盡可能少的模塊上使用Loader
{ 
    test: /\.js$/,
    loader: 'babel-loader',
}

看上面的代碼疾嗅,是我們?cè)谂渲?code>bable-loader時(shí)的代碼,如果這樣配置的話冕象,那么整個(gè)項(xiàng)目的js文件代承,都會(huì)做babel-loader的轉(zhuǎn)譯,但實(shí)際上渐扮,node_modules中的包都是幫我們轉(zhuǎn)譯過的论悴,重復(fù)的轉(zhuǎn)譯掖棉,勢必會(huì)降低webapck的打包速度,這時(shí)候我們就要通過設(shè)置babel-loader的作用范圍來提升打包速度膀估。

{ 
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
}

通過上面的配置幔亥,我們就不用再對(duì)node_modules中js文件做轉(zhuǎn)譯了,當(dāng)然了察纯,除了exclude選項(xiàng)排除某個(gè)范圍帕棉,我們還可以通過include選項(xiàng)去指定某個(gè)范圍,比如上面的代碼也可以改成

{ 
    test: /\.js$/,
    include: path.resolve(__dirname, '../src'),
    loader: 'babel-loader',
}

所以饼记,我們可以通過合理的使用exclude或者include這樣的配置項(xiàng)笤昨,去指定某一個(gè)loader的執(zhí)行范圍,從而降低了loader的執(zhí)行頻率握恳,loader的編譯過程被少量的執(zhí)行了瞒窒,那么webpack的打包速度自然也會(huì)得到提升。
不光babel-loader乡洼,其他loader也是可以通過具體的項(xiàng)目分析崇裁,做這樣的配置的。

將babel編譯過的文件緩存起來

babel-loader為我們提供了cacheDirectory參數(shù)束昵,可以參考官網(wǎng)對(duì)其做相應(yīng)配置

Plugin盡可能精簡并且可靠

我們應(yīng)該盡可能少的使用Plugin拔稳,并且還要保證其可靠性,舉個(gè)栗子锹雏。
我們?cè)谏a(chǎn)環(huán)境下的打包一般會(huì)需要通過MiniCssExtractPluginOptimizeCSSAssetsPlugin兩個(gè)插件來做樣式代碼的分離或者壓縮巴比,這也是十分必要的,當(dāng)然礁遵,如果你在本地環(huán)境下使用了CSS代碼的分離壓縮轻绞,不但沒有必要(因?yàn)楸镜卮a只有自己看,也不去在意其是否壓縮)佣耐,反而會(huì)降低打包的效率政勃,因?yàn)閃ebpack插件是基于webpack打包過程事件流的,沒一個(gè)插件的執(zhí)行兼砖,都會(huì)消耗性能奸远,降低效率,所以讽挟,如果非必要懒叛,就不要去使用一些插件了,如果你很有必要去使用某個(gè)插件耽梅,那么最好是使用Webapck官網(wǎng)提供的插件薛窥,因?yàn)楣俜降牟寮墙?jīng)過一些專門的性能測試的,相對(duì)于第三方的插件來說褐墅,性能會(huì)高一些拆檬,而第三方的插件,很有可能性能得不到保證妥凳,降低你的打包速度竟贯,所以,在使用一個(gè)插件之前逝钥,一定要做好選擇哦屑那!

resolve參數(shù)合理配置
  1. extensions

resolve參數(shù)是一個(gè)webpack配置項(xiàng),我們先開介紹一下這個(gè)配置項(xiàng)的使用艘款,比如現(xiàn)在有下面的文件目錄

|--src
  |--index.js
  |--child.jsx

我們想要在index.js中使用child.jsx可以這樣使用

import Child from './child.jsx'

但是我們可以通過配置resolve選項(xiàng)持际,來達(dá)到下面這樣的引用方式

import Child from './child'

如下:

module.exports = {
    resolve: {
        extensions: ['.js', '.jsx']
    },
}

上面的意思是,我們遇到'./child'這樣的字段后哗咆,會(huì)去當(dāng)前目錄下查找'js'后綴的文件蜘欲,沒有找到再去查找'jsx'后綴的文件,這樣我們就可以省去在引用的過程中寫前綴了晌柬,但是姥份,有些同學(xué)可能會(huì)不合理的配置resolve,比如

module.exports = {
    resolve: {
        extensions: ['css','jpg','.js', '.jsx']
    },
}

如果像上面這樣配置年碘,那么在你引入一個(gè)文件的時(shí)候澈歉,就會(huì)按照上面的裂變挨個(gè)的去查找,實(shí)際上屿衅,這樣是有性能損耗的
所以埃难,一般情況下,我們只有遇到j(luò)s或者jsx或者vue等等這樣邏輯型文件的時(shí)候才去配置到resolve中涤久,像css這樣的文件就不去配置了這樣涡尘,不但開發(fā)起來方便一些,同事性能上也會(huì)得到一些平衡响迂。

  1. mainFiles

在平時(shí)開發(fā)中大家一定也遇到過這樣的引用

import Child from './components/'

這時(shí)候悟衩,會(huì)自動(dòng)找到'components'文件夾下的'index.js'文件,假如我們現(xiàn)在的文件目錄如下

|--src
  |--components
    |--child.jsx
  |--index.jsx

我們?cè)趇ndex.jsx中想要通過

import Child from './components/'

上面這種引用方式引入'components'下的‘child.jsx’文件栓拜,那么我們可以做下面這樣的配置

module.exports = {
  resolve: {
      extensions: ['.js', '.jsx'],
      +++  mainFiles: ['index', 'child']
  },
}

這樣座泳,我們?cè)谝靡粋€(gè)文件夾時(shí),他就會(huì)默認(rèn)去找下面的index.js找不到再去找child.js了幕与。
但是挑势,這樣又會(huì)帶來性能問題,通過上面的配置后啦鸣,每次我們引入一個(gè)路徑的話潮饱,都會(huì)去做一遍文件的匹配,所以我們要根據(jù)自己的需要诫给,平衡好性能和開發(fā)方便后再做相應(yīng)的配置香拉,一般來說啦扬,我們不需要配置這個(gè)項(xiàng)

  1. alias

在一些社區(qū)腳手架中,我們還會(huì)見到下面這樣的引用方式

import Child from '@/component/'

其配置如下

module.exports = {
    resolve: {
        extensions: ['.js', '.jsx'],
        +++ alias: {
            '@': path.resolve(__dirname, '../src')
        }
    },
}

意思是凫碌,我們用‘@’代替了根目錄下的src目錄扑毡,這樣你會(huì)在開發(fā)的時(shí)候提升一些開發(fā)效率。同樣盛险,他也會(huì)帶來一些性能上的問題瞄摊,所以,大家依然需要平衡好開發(fā)效率和打包效率苦掘,有針對(duì)性的去使用
通過上面换帜,舉了三個(gè)栗子,說明了resolve配置項(xiàng)對(duì)于開發(fā)效率的提升幫助鹤啡,同事他也具有一點(diǎn)的性能問題惯驼,大家在使用的過程中,要在做好平衡递瑰,按照需要去做相應(yīng)的配置跳座。

使用DllPlugin提高打包速度

我先對(duì)我手上一個(gè)簡單的項(xiàng)目做個(gè)打包,記錄下打包時(shí)間如下

buildtime99.png

基本時(shí)間穩(wěn)定在1500ms,我們暫認(rèn)定當(dāng)前情況下的打包速度為1300ms,我的代碼現(xiàn)在是這樣的

import React from 'react'
import ReactDom from 'react-dom'
import _ from 'lodash'

const App = () => {
    return (
        <div>
            <div>{_.join(['hello','world'], ' ')}</div>
        </div>
    )
}
ReactDom.render(<App/>, document.getElementById('root'))

其中像react,react-dom,lodash這樣的庫泣矛,是基本不會(huì)改變的疲眷,但是現(xiàn)在,我們每一次打包都要對(duì)其進(jìn)行分析您朽,都要消耗一定的時(shí)間狂丝,于是我們就想,可以把第三方庫單獨(dú)打包為一個(gè)文件哗总,只在第一次打包的時(shí)候做分析几颜,后面就使用第一次打包的結(jié)果這樣就可以提高打包速度了,我們以這個(gè)為思路讯屈,展開這次的優(yōu)化蛋哭。

  1. 配置第三方庫單獨(dú)打包

我們?cè)賱?chuàng)建一個(gè)webpack.dll.js的配置文件,內(nèi)容如下

const path = require('path')
module.exports = {
    mode: 'production',
    entry: {
        vendors: ['react', 'react-dom', 'lodash']
    },
    output: {
        filename: '[name].dll.js',
        path: path.resolve(__dirname, '../dll'),
        library: '[name]'
    }
}

上面的意思是涮母,我們將幾個(gè)第三方庫做單獨(dú)的打包谆趾,并以Library的形式導(dǎo)出,這時(shí)候會(huì)在根目錄下生成一個(gè)'dll'的文件叛本。我們期望將該文件在最終生成的index.html中以全局變量的形式引入沪蓬。所以還需要在原有的打包配置中,配置一個(gè)插件来候,來動(dòng)態(tài)的引入我們生成的第三方庫跷叉,因?yàn)楝F(xiàn)在的第三方庫是以Library的形式存在于項(xiàng)目中,并以一個(gè)‘vendors’變量全局暴露。這樣我們就可以以全局變量的形式訪問第三方庫

  1. 配置add-asset-html-webpack-plugin

我們安裝這個(gè)webapck插件云挟,并配置如下

module.exports = {
  plugins: [
        new AddAssetHtmlWebpackPlugin({
            filepath: path.resolve(__dirname, '../dll/vendors.dll.js')
        })
  ]
}

意思是我們通過上面這個(gè)插件梆砸,就可以為生成的index.html引入我們單獨(dú)打包的第三方庫,配置成功后园欣,啟動(dòng)項(xiàng)目你會(huì)發(fā)現(xiàn)源碼中已經(jīng)引入‘vendors.dll.js’了帖世。

html.png

并且在也可以全局訪問一個(gè)‘vendors’變量(因?yàn)槲覀兪且訪ibrary的形式打包,并暴露出一個(gè)vendors變量)
到這里俊庇,我們實(shí)現(xiàn)了一個(gè)第三方模塊只打包一次的目標(biāo),但是現(xiàn)在還不能滿足我們最初的鸡挠,‘第三方模塊只打包一次辉饱,且以后每次都使用’的目標(biāo),現(xiàn)在我們的項(xiàng)目中拣展,其中還是使用的'node_modules'里面的內(nèi)容彭沼,那么怎么才能讓業(yè)務(wù)代碼使用我們處理過的第三方模塊呢?

  1. 使用Dllplugin做分析

我們使用Dllplugin生成一個(gè)映射备埃,操作如下
對(duì)webpack.dll.js做下修改

const path = require('path')
const webpack = require('webpack')
module.exports = {
    mode: 'production',
    entry: {
        vendors: ['react', 'react-dom', 'lodash']
    },
    output: {
        filename: '[name].dll.js',
        path: path.resolve(__dirname, '../dll'),
        library: '[name]'
    },
    plugins: [
        new webpack.DllPlugin({
            name: '[name]',
            path: path.resolve(__dirname, '../dll/[name].manifest.json')
        })
    ]
}

我們配置一個(gè)Dllplugin插件姓惑,需要注意的是DllPlugin中的name屬性,一定要個(gè)output中的library屬性一致按脚,意思是于毙,我們要對(duì)生成的library做一個(gè)分析分析的結(jié)果放到dll下的‘vendors.manifest.json’中。這時(shí)候再運(yùn)行dll打包辅搬,就會(huì)看到這個(gè)'vendors.manifest.json'文件了唯沮。
到這里我們想利用上面生成的全局變量,和現(xiàn)在生成的映射文件堪遂,我們是否可以實(shí)現(xiàn)在業(yè)務(wù)代碼中介蛉,如果發(fā)現(xiàn)引用的模塊是來自我們處理過的第三方模塊,就使用我們已經(jīng)打包過的包溶褪,反之才從node_modules中取

  1. 配置DllReferencePlugin

要想實(shí)現(xiàn)上面的設(shè)想币旧,我們還需要在打包配置文件中,做DllReferencePlugin插件的配置

module.exports = {
  plugins: [
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname, '../dll/vendors.manifest.json')
        })
   ]
}

做了上面的配置后猿妈,我們打包時(shí)的原理變成了這樣:在打包時(shí)吹菱,當(dāng)遇到第三方模塊,他會(huì)去到映射文件中去找是否存在于我們單獨(dú)打包的第三方庫中彭则,如果存在毁葱,就從上面操作中暴露的全局變量中取,如果不存在贰剥,才從node_moudules中取倾剿,這時(shí)候,我們做一下打包時(shí)間對(duì)比


dllplugin.png

時(shí)間變成了900多毫秒,可以把上面的配置注釋掉前痘,再去看一下打包時(shí)間


nodll.png

時(shí)間又變成了1400多毫秒凛捏,由此可見,使用DllPlugin對(duì)于性能的提升還是很明顯的芹缔。
這個(gè)配置項(xiàng)講的有點(diǎn)繞坯癣,下面針對(duì)這個(gè)插件的配置,我們做個(gè)小總結(jié)

  • 通過dll配置文件單獨(dú)將第三方庫打包為一個(gè)library形式最欠,暴露一個(gè)全局變量出來
  • 通過DllPlugin插件示罗,對(duì)打包文件做一個(gè)分析,生成一個(gè)映射文件
  • 在項(xiàng)目打包配置文件中芝硬,配置AddAssetHtmlWebpackPluginDllReferencePlugin蚜点,將映射關(guān)系引入進(jìn)index.html中

主要操作就是上面的三點(diǎn)了。下面我再對(duì)這個(gè)插件做一點(diǎn)擴(kuò)展拌阴,上面我們是把三個(gè)第三方模塊都打包到了绍绘,其實(shí)我們可以分開打包

module.exports = {
    entry: {
        lodash: ['lodash'],
        react: ['react', 'react-dom']
    },
}

分開后,自然生成的library文件不一樣了迟赃,映射文件也不一樣了陪拘,所以我們還得再業(yè)務(wù)打包文件中做出更改

module.exports = {
  plugins: [
        new AddAssetHtmlWebpackPlugin({
            filepath: path.resolve(__dirname, '../dll/lodash.dll.js')
        }),
        new AddAssetHtmlWebpackPlugin({
            filepath: path.resolve(__dirname, '../dll/react.dll.js')
        }),
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname, '../dll/lodash.manifest.json')
        }),
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname, '../dll/react.manifest.json')
        })
  ]
}

大家一定也發(fā)現(xiàn)了,其實(shí)這樣的配置看起來是很臃腫的纤壁,于是我們可以這樣修改我們的配置

const plugins = [ // 定義一個(gè)數(shù)組左刽,將基礎(chǔ)的插件寫入
    new HtmlWebpackPlugin({
        template: 'src/index.html'
    }),
    new CleanWebpackPlugin(),
    new webpack.ProvidePlugin({
        $: 'jquery'
    })
]
// 利用NodeJs文件模塊,分析dll文件夾下的文件酌媒,并動(dòng)態(tài)插入
const files = fs.readdirSync(path.resolve(__dirname, '../dll'))
console.log(files) // 可以在這里查看結(jié)果感受一下
files.forEach(file => {
    if(/.*\.dll.js/.test(file)) {
        plugins.push(
            new AddAssetHtmlWebpackPlugin({
                filepath: path.resolve(__dirname, '../dll', file)
            })            
        )
    }
    if(/.*\.manifest.json/.test(file)) {
        plugins.push(
            new webpack.DllReferencePlugin({
                manifest: path.resolve(__dirname, '../dll', file)
            })           
        )
    }
})

這樣我們就不用手寫的悠反,如果你的dll于變動(dòng),只需要重新打包dll即可馍佑,不用再手動(dòng)修改插件了斋否。

thread-loader和happypack

因?yàn)槭芟抻贜ode的單線程運(yùn)行,所以webpack的打包也是單線程的拭荤,使用HappyPack可以將Loader的同步執(zhí)行轉(zhuǎn)為并行茵臭,從而執(zhí)行Loader時(shí)的編譯等待時(shí)間
同時(shí)也可以使用webpack4官網(wǎng)提供的thread-loader來對(duì)有些耗時(shí)的loader做相應(yīng)的處理,這里我將不再帶大家熟悉其API舅世,可以到對(duì)應(yīng)的官網(wǎng)去參照其使用方法旦委。

合理使用Source Map

Source Map為我們打包后的代碼和源碼提供了一種個(gè)映射關(guān)系,但是Source Map也會(huì)造成一些性能的問題雏亚,為了同時(shí)兼顧打包性能和開發(fā)調(diào)試方便缨硝,請(qǐng)使用合理的Source Map配置,這里可以參考我之前關(guān)于Source Map的講解SourceMap配置

開發(fā)環(huán)境內(nèi)存編譯

我們知道罢低,我們?cè)诒镜氐捻?xiàng)目中查辩,一般使用dev Server在本地起一個(gè)服務(wù)胖笛,而使用dev Server是不需要將dist文件打包進(jìn)硬盤的,而是打包進(jìn)內(nèi)存里宜岛,從內(nèi)存里讀取文件的速度肯定是比硬盤快的多的长踊,因?yàn)槠綍r(shí)大家有意無意的已經(jīng)這么實(shí)踐,這里還是要提一下萍倡,知道其中的優(yōu)化點(diǎn)

開發(fā)環(huán)境無用插件剔除

有些Webpack插件是針對(duì)于線上打包模式的身弊,比如代碼壓縮,比如CSS分離壓縮等列敲,但是如果你在本地環(huán)境使用了這樣的插件阱佛,將降低你的打包速度,同時(shí)有些插件在本地模式下使用戴而,也是沒有意義的凑术,比如代碼壓縮。

降低打包體積

降低打包體積填硕,不僅可以讓打包后的項(xiàng)目運(yùn)行更快麦萤,還可以對(duì)打包速度有所提升鹿鳖。我在下面將做詳細(xì)的介紹

上面為大家介紹了幾種提升打包速度的方法扁眯,用來優(yōu)化我們本地開發(fā)的效率,其中將到的DllPlugin也是內(nèi)容比較多翅帜,需要主要的是姻檀,這個(gè)插件僅在開發(fā)環(huán)境下生效,并且在開發(fā)中涝滴,隨著后續(xù)weebpack版本的更新绣版,可能會(huì)引入一些緩存機(jī)制,到時(shí)候DllPlugin就不再使用了歼疮,這里我們大篇幅介紹他杂抽,希望大家能認(rèn)識(shí)到并熟悉這種方式,用不用看大家


打包大小優(yōu)化

上面提到韩脏,打包大小的優(yōu)化主要對(duì)于產(chǎn)品的體驗(yàn)有很大的提升缩麸,那么我們有哪些手段可以控制打包的大小,從而讓產(chǎn)品運(yùn)行很流暢呢赡矢?

tree shaking

我們知道杭朱,webapck4默認(rèn)在production模式下開啟tree Shaking,用來刪除調(diào)那些無效的引入吹散,從而減小打包代碼的體積弧械,當(dāng)然你也可以嘗試在本地模式下配置,不過沒啥太實(shí)際的作用空民,具體可參考我之前關(guān)于Tree Shaking的講解文章刃唐。

代碼壓縮

webpack4在production模式下默認(rèn)開啟代碼壓縮。這一點(diǎn)大家要知道

代碼分割

我們可以使用代碼分割,將固定不變的一些代碼如node_moudles中的代碼單獨(dú)打包唁桩,從而降低main.js的大小闭树,利用瀏覽器的緩存機(jī)制,提高首屏加載的速度荒澡。具體的代碼报辱,可以看我之前關(guān)于Split code的講解文章文章一文章二

按需加載

按需加載单山,也是個(gè)比較大的概念了碍现,我舉幾個(gè)常見的按需加載場景。

  1. polyfill按需加載

我們知道米奸,polyfill實(shí)際是一種webpack shaming方案昼接,如果我們不做處理,將是全量的引入所有的轉(zhuǎn)譯語法悴晰,但實(shí)際項(xiàng)目中慢睡,我們不一定都用的到,這時(shí)候需要做一下按需加載的配置铡溪,可以配置@babel/preset-envuseBuiltIns:usage漂辐,具體的內(nèi)容可以參考我之前關(guān)于babel的文章講解babel

  1. UI組件庫的按需加載

現(xiàn)在社區(qū)的大部分組件都是支持按需加載配置,或者tree shaking的棕硫,這樣我們就不需要將整個(gè)UI庫引入了髓涯,因?yàn)槟憧赡茼?xiàng)目中用不到所有的,具體我們可以參考babel-plugin-import的使用方法哈扮,或者組件庫推薦的按需加載方案

  1. 路由按需加載

路由的按需加載也叫路由懶加載纬纪,也就是,只有當(dāng)我們?cè)L問到該頁面時(shí)滑肉,才加載該頁面的資源包各,這個(gè)方案其實(shí)不影響打包大小,算是一種代碼分割的方案靶庙,我們通過異步的加載路由下對(duì)應(yīng)的組件資源问畅,利用代碼分割單獨(dú)打包。這里可以自己去看一下惶洲,不同的框架對(duì)應(yīng)的路由懶加載方案

寫在后面

本文用很大的篇幅介紹了Webapck的性能優(yōu)化按声,盡量避免知識(shí)點(diǎn)過散,不利于總結(jié)恬吕,其實(shí)签则,關(guān)于webpack的打包優(yōu)化方案,還有好多铐料,甚至到webpack5的時(shí)候渐裂,webpack的打包性能又會(huì)優(yōu)化不少豺旬,像上面提到的DllPlugiin可能將不再使用,隨著技術(shù)的更新柒凉,Webpack優(yōu)化的手段也將越來越豐富族阅,大家可以根據(jù)自己的需要去拓展更多的優(yōu)化手段。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末膝捞,一起剝皮案震驚了整個(gè)濱河市坦刀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔬咬,老刑警劉巖鲤遥,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異林艘,居然都是意外死亡盖奈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門狐援,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钢坦,“玉大人,你說我怎么就攤上這事啥酱〉迹” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵懈涛,是天一觀的道長逛万。 經(jīng)常有香客問我泳猬,道長批钠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任得封,我火速辦了婚禮埋心,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忙上。我一直安慰自己拷呆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布疫粥。 她就那樣靜靜地躺著茬斧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪梗逮。 梳的紋絲不亂的頭發(fā)上项秉,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音慷彤,去河邊找鬼寥院。 笑死,一個(gè)胖子當(dāng)著我的面吹牛粉渠,可吹牛的內(nèi)容都是我干的二拐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼笔时,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起哗蜈,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坠韩,沒想到半個(gè)月后恬叹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡同眯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年绽昼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片须蜗。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡硅确,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出明肮,到底是詐尸還是另有隱情菱农,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布柿估,位于F島的核電站循未,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏秫舌。R本人自食惡果不足惜的妖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望足陨。 院中可真熱鬧嫂粟,春花似錦、人聲如沸墨缘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽镊讼。三九已至宽涌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝶棋,已是汗流浹背卸亮。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嚼松,地道東北人嫡良。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓锰扶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寝受。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坷牛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355