基于vue-cli3.0構(gòu)建功能完善的移動(dòng)端架子

歡迎關(guān)注小程序“進(jìn)階的大前端”虱饿,800多道前端面試題在線(xiàn)查看

image

基于vue-cli3.0構(gòu)建功能完善的移動(dòng)端架子,主要功能包括

  1. webpack 打包擴(kuò)展
  2. css:sass支持采记、normalize.css、_mixin.scss、_variables.scss
  3. vw砍聊、rem布局
  4. 跨域設(shè)置
  5. eslint設(shè)置
  6. cdn引入
  7. 路由設(shè)計(jì)、登錄攔截
  8. axios贰军、api 設(shè)計(jì)
  9. vuex狀態(tài)管理

項(xiàng)目地址: vue-cli3-H5

demo地址: https://zhouyupeng.github.io/vuecli3H5/#/

webpack 打包擴(kuò)展

vue-cli3.*后目錄結(jié)構(gòu)大改玻蝌,去除了以往的build,config文件夾,要實(shí)現(xiàn)配置的改動(dòng)在根目錄下增加vue.config.js進(jìn)行配置

css:sass支持词疼、normalize.css俯树、_mixin.scss、_variables.scss

使用的css預(yù)處理器是sass贰盗,對(duì)于css mixin许饿,變量這里做了全局引入,并且引入normalize.css 使HTML元素樣式在跨瀏覽器上表現(xiàn)得的高度一致性
vue.config.js配置

css: {
        // 是否使用css分離插件 ExtractTextPlugin
        extract: true,
        // 開(kāi)啟 CSS source maps?
        sourceMap: false,
        // css預(yù)設(shè)器配置項(xiàng)
        // 啟用 CSS modules for all css / pre-processor files.
        modules: false,
            sass: {
                data: '@import "style/_mixin.scss";@import "style/_variables.scss";' // 全局引入
            }
        }
    }

vw童太、rem布局

對(duì)于移動(dòng)端適配方案使用的是網(wǎng)易新聞的方法,
使用vw + rem布局

/**
750px設(shè)計(jì)稿
    取1rem=100px為參照米辐,那么html元素的寬度就可以設(shè)置為width: 7.5rem,于是html的font-size=deviceWidth / 7.5
**/
html {
    font-size: 13.33333vw
}

@media screen and (max-width: 320px) {
    html {
        font-size: 42.667PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 321px) and (max-width:360px) {
    html {
        font-size: 48PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 361px) and (max-width:375px) {
    html {
        font-size: 50PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 376px) and (max-width:393px) {
    html {
        font-size: 52.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 394px) and (max-width:412px) {
    html {
        font-size: 54.93PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 413px) and (max-width:414px) {
    html {
        font-size: 55.2PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 415px) and (max-width:480px) {
    html {
        font-size: 64PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 481px) and (max-width:540px) {
    html {
        font-size: 72PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 541px) and (max-width:640px) {
    html {
        font-size: 85.33PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 641px) and (max-width:720px) {
    html {
        font-size: 96PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 721px) and (max-width:768px) {
    html {
        font-size: 102.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 769px) {
    html {
        font-size: 102.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 769px) {
    html {
        font-size: 102.4PX;

        #app {
            margin: 0 auto
        }
    }


}

vue.config.js配置

loaderOptions: {
    postcss: {
        // 這是rem適配的配置
        plugins: [
            require('postcss-px2rem')({
                remUnit: 100
            })
        ]
    }
}

開(kāi)發(fā)時(shí)跨域設(shè)置

devServer: {
        open: true, // 啟動(dòng)服務(wù)后是否打開(kāi)瀏覽器
        host: '127.0.0.1',
        port: 8088, // 服務(wù)端口
        https: false,
        hotOnly: false,
        proxy: 'https://easy-mock.com/' // 設(shè)置代理
    }

配置完后书释,本地開(kāi)發(fā)環(huán)境的axios的baseUrl要寫(xiě)為 '' 翘贮,即空字符串。
發(fā)布到線(xiàn)上時(shí)如果前端代碼不是和后臺(tái)api放在同源下的爆惧,后臺(tái)還需做跨域處理狸页,

eslint standard設(shè)置

使用的是JavaScript standard 代碼規(guī)范,一個(gè)好的編碼風(fēng)格它可以幫助減少團(tuán)隊(duì)之間的摩擦,代碼閱讀起來(lái)也更加清爽扯再,更加可讀性芍耘,不要覺(jué)得煩,用了都說(shuō)好熄阻。
這是 JavaScript standard 代碼規(guī)范的全文

自定義配置斋竞,在.eslintrc.js里修改,這里是我給出的配置秃殉,4個(gè)空格縮進(jìn)坝初,不檢查結(jié)尾分號(hào),關(guān)閉單var 聲明,可自行配置

rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    indent: [
        'error',
        4,
        {
            SwitchCase: 1
        }
    ],
    semi: 0, // 不檢查結(jié)尾分號(hào),
    // 強(qiáng)制使用單引號(hào)
    quotes: ['error', 'single'],
    // 關(guān)閉函數(shù)名與后面括號(hào)間必須空格規(guī)則
    'space-before-function-paren': 0,
    // 關(guān)閉var 聲明钾军,每個(gè)聲明占一行規(guī)則鳄袍。
    'one-var': 0
    }

cdn引入

對(duì)于 vue、vue-router吏恭、vuex拗小、axios等等這些不經(jīng)常改動(dòng)的庫(kù)、我們讓webpack不對(duì)他們進(jìn)行打包樱哼,通過(guò)cdn引入哀九,可以減少代碼的大小剿配、也可以減少服務(wù)器的帶寬
這里使用的是360的cdn,附上一份公共cdn評(píng)測(cè)文章 點(diǎn)我

vue.config.js配置

const externals = {
    vue: 'Vue',
    'vue-router': 'VueRouter',
    vuex: 'Vuex',
    'mint-ui': 'MINT',
    axios: 'axios'

}

const cdn = {
    // 開(kāi)發(fā)環(huán)境
    dev: {
        css: [
            'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
        ],
        js: []
    },
    // 生產(chǎn)環(huán)境
    build: {
        css: [
            'https://lib.baomitu.com/mint-ui/2.2.13/style.min.css'
        ],
        js: [
            'https://lib.baomitu.com/vue/2.6.6/vue.min.js',
            'https://lib.baomitu.com/vue-router/3.0.1/vue-router.min.js',
            'https://lib.baomitu.com/vuex/3.0.1/vuex.min.js',
            'https://lib.baomitu.com/axios/0.18.0/axios.min.js',
            'https://lib.baomitu.com/mint-ui/2.2.13/index.js'
        ]
    }
}

configureWebpack: config => {
        if (isProduction) {
            // externals里的模塊不打包
            Object.assign(config, {
                externals: externals
            })
       
        } else {
            // 為開(kāi)發(fā)環(huán)境修改配置...
        }
    },
chainWebpack: config => {
    // 對(duì)vue-cli內(nèi)部的 webpack 配置進(jìn)行更細(xì)粒度的修改。
    // 添加CDN參數(shù)到htmlWebpackPlugin配置中勾栗, 詳見(jiàn)public/index.html 修改
    config.plugin('html').tap(args => {
        if (process.env.NODE_ENV === 'production') {
            args[0].cdn = cdn.build
        }
        if (process.env.NODE_ENV === 'development') {
            args[0].cdn = cdn.dev
        }
        return args
    })
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <!-- DNS預(yù)解析 -->
    <link rel="dns-prefetch"  />
    <meta name="viewport"
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=0,minimal-ui,viewport-fit=cover" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <!-- 使用CDN加速的CSS文件惨篱,配置在vue.config.js下 -->
    <% for (var i in
    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
    <% } %>

    <title>vuedemo</title>
</head>

<body>
    <noscript>
        <strong>We're sorry but vuedemo doesn't work properly without JavaScript
            enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
    <% for (var i in
    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
    <% } %>

    <!-- built files will be auto injected -->
</body>

</html>

路由設(shè)計(jì)围俘、登錄攔截

const router = new Router({
    routes: [
        {
            path: '/',
            name: 'home',
            component: Home,
            meta: {
                auth: false, // 是否需要登錄
                keepAlive: true // 是否緩存組件
            }
        },
        {
            path: '/about',
            name: 'about',
            component: () =>
                import(/* webpackChunkName: "about" */ './views/About.vue'),
            meta: {
                auth: true,
                keepAlive: true
            }
        },
        {
            path: '/login',
            name: 'login',
            component: () =>
                import(/* webpackChunkName: "login" */ './views/login.vue'),
            meta: {
                auth: false,
                keepAlive: true
            }
        },
        {
            path: '*', // 未匹配到路由時(shí)重定向
            redirect: '/',
            meta: {
                // auth: true,
                // keepAlive: true
            }
        }
    ]
})

// 全局路由鉤子函數(shù) 對(duì)全局有效
router.beforeEach((to, from, next) => {
    let auth = to.meta.auth
    let token = store.getters['login/token'];

    if (auth) { // 需要登錄
        if (token) {
            next()
        } else {
            next({
                name: 'login',
                query: {
                    redirect: to.path
                }
            })
        }
    } else {
        next()
    }
})

在meta中設(shè)置是否需要登錄以及是否緩存當(dāng)前組件,
在router.beforeEac路由鉤子函數(shù)中對(duì)登錄權(quán)限判斷琢融,沒(méi)有登錄的跳到登錄頁(yè)面界牡,并且把當(dāng)前頁(yè)面?zhèn)鬟^(guò)去,登錄后跳回這個(gè)頁(yè)面漾抬。

對(duì)于頁(yè)面緩存的在app.vue里進(jìn)行處理

<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

axios宿亡、api 設(shè)計(jì)

對(duì)于axios的設(shè)計(jì)主要是請(qǐng)求攔截器, respone攔截器纳令,以及get,post的二次封裝

axios.defaults.timeout = 12000 // 請(qǐng)求超時(shí)時(shí)間
axios.defaults.baseURL = process.env.VUE_APP_BASE_API

axios.defaults.headers.post['Content-Type'] =
    'application/x-www-form-urlencoded;charset=UTF-8' // post請(qǐng)求頭的設(shè)置
// axios 請(qǐng)求攔截器
axios.interceptors.request.use(
    config => {
        // 可在此設(shè)置要發(fā)送的token
        let token = store.getters['login/token'];
        token && (config.headers.token = token)
        Indicator.open('數(shù)據(jù)加載中')
        return config
    },
    error => {
        return Promise.error(error)
    }
)
// axios respone攔截器
axios.interceptors.response.use(
    response => {
        // 如果返回的狀態(tài)碼為200挽荠,說(shuō)明接口請(qǐng)求成功,可以正常拿到數(shù)據(jù)
        // 否則的話(huà)拋出錯(cuò)誤 結(jié)合自身業(yè)務(wù)和后臺(tái)返回的接口狀態(tài)約定寫(xiě)respone攔截器
        Indicator.close()
        console.log('response', response);
        if (response.status === 200 && response.data.code === 0) {
            return Promise.resolve(response)
        } else {
            Toast({
                message: response.data.msg,
                position: 'middle',
                duration: 2000
            });
            return Promise.reject(response)
        }
    },
    error => {
        Indicator.close()
        const responseCode = error.response.status
        switch (responseCode) {
            // 401:未登錄
            case 401:
                break
            // 404請(qǐng)求不存在
            case 404:
                Toast({
                    message: '網(wǎng)絡(luò)請(qǐng)求不存在',
                    position: 'middle',
                    duration: 2000
                });
                break
            default:
                Toast({
                    message: error.response.data.message,
                    position: 'middle',
                    duration: 2000
                });
        }
        return Promise.reject(error)
    }
)
/**
 * 封裝get方法平绩,對(duì)應(yīng)get請(qǐng)求
 * @param {String} url [請(qǐng)求的url地址]
 * @param {Object} params [請(qǐng)求時(shí)攜帶的參數(shù)]
 */
function get (url, params = {}) {
    return new Promise((resolve, reject) => {
        axios
            .get(url, {
                params: params
            })
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
}
/**
 * post方法圈匆,對(duì)應(yīng)post請(qǐng)求
 * @param {String} url [請(qǐng)求的url地址]
 * @param {Object} params [請(qǐng)求時(shí)攜帶的參數(shù)]
 */
function post (url, params) {
    return new Promise((resolve, reject) => {
        axios
            .post(url, qs.stringify(params))
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
}

為了方便管理api路徑,這里把所以請(qǐng)求都放在了api文件夾下捏雌,如

import { get, post } from '@/axios/http.js'
function getIndex (params) {
    return get('/mock/5cb48c7ed491cd741c54456f/base/index', params)
}
function login(params) {
    return post('/mock/5cb48c7ed491cd741c54456f/base/login', params)
}
export {
    getIndex,
    login
}

其他

去除console.log

裝uglifyjs-webpack-plugin插件

 // 上線(xiàn)壓縮去除console等信息
config.plugins.push(
    new UglifyJsPlugin({
        uglifyOptions: {
            compress: {
                warnings: false,
                drop_console: true,
                drop_debugger: false,
                pure_funcs: ['console.log'] // 移除console
            }
        },
        sourceMap: false,
        parallel: true
    })
)

設(shè)置alias目錄別名

在項(xiàng)目中經(jīng)常會(huì)引用各個(gè)地方的文件跃赚,配置后可以更加方便的引入了

config.resolve.alias
            .set('assets', '@/assets')
            .set('components', '@/components')
            .set('view', '@/view')
            .set('style', '@/style')
            .set('api', '@/api')
            .set('store', '@/store')

環(huán)境變量和模式

在一個(gè)產(chǎn)品的前端開(kāi)發(fā)過(guò)程中,一般來(lái)說(shuō)會(huì)經(jīng)歷本地開(kāi)發(fā)性湿、測(cè)試腳本纬傲、開(kāi)發(fā)自測(cè)、測(cè)試環(huán)境肤频、預(yù)上線(xiàn)環(huán)境叹括,然后才能正式的發(fā)布。對(duì)應(yīng)每一個(gè)環(huán)境可能都會(huì)有所差異宵荒,比如說(shuō)服務(wù)器地址汁雷、接口地址、websorket地址…… 等等骇扇。在各個(gè)環(huán)境切換的時(shí)候摔竿,就需要不同的配置參數(shù),所以就可以用環(huán)境變量和模式少孝,來(lái)方便我們管理继低。

.env                # 在所有的環(huán)境中被載入
.env.local          # 在所有的環(huán)境中被載入,但會(huì)被 git 忽略
.env.[mode]         # 只在指定的模式中被載入
.env.[mode].local   # 只在指定的模式中被載入稍走,但會(huì)被 git 忽略

自定義的變量VUE_APP_開(kāi)頭袁翁,兩個(gè)特殊的變量:

  1. NODE_ENV - 會(huì)是 "development"柴底、"production" 或 "test" 中的一個(gè)。具體的值取決于應(yīng)用運(yùn)行的模式粱胜。
  2. BASE_URL - 會(huì)和 vue.config.js 中的 baseUrl 選項(xiàng)相符柄驻,即你的應(yīng)用會(huì)部署到的基礎(chǔ)路徑。

如我們定義的.env

NODE_ENV = 'development'
BASE_URL = '/'
VUE_APP_BASE_API = ''

.env.production

NODE_ENV = 'production'
BASE_URL = './'
VUE_APP_BASE_API = 'https://easy-mock.com/'

在項(xiàng)目中可以用process.env.VUE_APP_*焙压,如process.env.VUE_APP_BASE_API獲取到定義的值

全局引入filter

把多個(gè)地方用到的過(guò)濾器寫(xiě)在一個(gè)js里面鸿脓,復(fù)用代碼。

// 過(guò)濾日期格式涯曲,傳入時(shí)間戳野哭,根據(jù)參數(shù)返回不同格式
const formatTimer = function(val, hours) {
    if (val) {
        var dateTimer = new Date(val * 1000)
        var y = dateTimer.getFullYear()
        var M = dateTimer.getMonth() + 1
        var d = dateTimer.getDate()
        var h = dateTimer.getHours()
        var m = dateTimer.getMinutes()
        M = M >= 10 ? M : '0' + M
        d = d >= 10 ? d : '0' + d
        h = h >= 10 ? h : '0' + h
        m = m >= 10 ? m : '0' + m
        if (hours) {
            return y + '-' + M + '-' + d + ' ' + h + ':' + m
        } else {
            return y + '-' + M + '-' + d
        }
    }
}
export default {
    formatTimer
}

main.js引入

import filters from './filters/index'
// 注入全局過(guò)濾器
Object.keys(filters).forEach(item => {
    Vue.filter(item, filters[item])
})

使用

{{1555851774 | formatTimer()}}

vue中使用mock.js

查看我以前寫(xiě)的文章點(diǎn)擊我

wepback的可視化資源分析工具插件---webpack-bundle-analyzer

用來(lái)分析哪些模塊引入了哪些代碼,進(jìn)行有目的性的優(yōu)化代碼

在打包環(huán)境中加幻件,使用命令npm run build --report

if (process.env.npm_config_report) {
    config.plugins.push(new BundleAnalyzerPlugin())
}
111

代碼地址

項(xiàng)目地址: vue-cli3-H5

demo地址: https://zhouyupeng.github.io/vuecli3H5/#/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拨黔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子绰沥,更是在濱河造成了極大的恐慌篱蝇,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徽曲,死亡現(xiàn)場(chǎng)離奇詭異零截,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)疟位,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)瞻润,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人甜刻,你說(shuō)我怎么就攤上這事绍撞。” “怎么了得院?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵傻铣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我祥绞,道長(zhǎng)非洲,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任蜕径,我火速辦了婚禮两踏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兜喻。我一直安慰自己梦染,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著帕识,像睡著了一般泛粹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肮疗,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天晶姊,我揣著相機(jī)與錄音,去河邊找鬼伪货。 笑死们衙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的超歌。 我是一名探鬼主播砍艾,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼巍举!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起凝垛,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤懊悯,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后梦皮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體炭分,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年剑肯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捧毛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡让网,死狀恐怖呀忧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情溃睹,我是刑警寧澤而账,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站因篇,受9級(jí)特大地震影響泞辐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜竞滓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一咐吼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧商佑,春花似錦锯茄、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)俗冻。三九已至,卻和暖如春牍颈,著一層夾襖步出監(jiān)牢的瞬間迄薄,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工煮岁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留讥蔽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓画机,卻偏偏與公主長(zhǎng)得像冶伞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子步氏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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