tensorflow-TFRecord 文件詳解

TFRecord 是 tensorflow 內置的文件格式,它是一種二進制文件,具有以下優點:

1. 統一各種輸入文件的操作

2. 更好的利用內存,方便複製和移動

3. 將二進制數據和標籤(label)存儲在同一個文件中

 

引言

在了解如下操作後進一步詳細講解TFRecord

 

tf.train.Int64List(value=list_data)

它的作用是 把 list 中每個元素轉換成 key-value 形式,

注意,輸入必須是 list,且 list 中元素類型要相同,且與 Int 保持一致;

# value = tf.constant([1, 2])     ### 這會報錯的
ss = 1               ### Int64List 對應的元素只能是 int long,其他同理
tt = 2
out1 = tf.train.Int64List(value = [ss, tt])
print(out1)
# value: 1
# value: 2

ss = [1 ,2]
out2 = tf.train.Int64List(value = ss)
print(out2)
# value: 1
# value: 2

 

同類型的 方法還有 2 個

tf.train.FloatList
tf.train.BytesList

 

tf.train.Feature(int64_list=)

它的作用是 構建 一種類型的特徵集,比如 整型

out = tf.train.Feature(int64_list=tf.train.Int64List(value=[33, 22]))
print(out)
# int64_list {
#   value: 33
#   value: 22
# }

也可以是其他類型

tf.train.Feature(float_list=tf.train.FloatList())
tf.train.Feature(bytes_list=tf.train.BytesList())

 

tf.train.Features(feature=dict_data)

它的作用是 構建 多種類型 的特徵集,可以 dict 格式表達 多種類型

ut = tf.train.Features(feature={
                            "suibian": tf.train.Feature(int64_list=tf.train.Int64List(value=[1, 2, 4])),
                            "a": tf.train.Feature(float_list=tf.train.FloatList(value=[5., 7.]))
                        })
print(out)
# feature {
#   key: "a"
#   value {
#     float_list {
#       value: 5.0
#       value: 7.0
#     }
#   }
# }
# feature {
#   key: "suibian"
#   value {
#     int64_list {
#       value: 1
#       value: 2
#       value: 4
#     }
#   }
# }

 

tf.train.Example(features=tf.train.Features())

它的作用是創建一個 樣本,Example 對應一個樣本

example = tf.train.Example(features=
                           tf.train.Features(feature={
                               'a': tf.train.Feature(int64_list=tf.train.Int64List(value=range(2))),
                               'b': tf.train.Feature(bytes_list=tf.train.BytesList(value=[b'm',b'n']))
                           }))
print(example)
# features {
#   feature {
#     key: "a"
#     value {
#       int64_list {
#         value: 0
#         value: 1
#       }
#     }
#   }
#   feature {
#     key: "b"
#     value {
#       bytes_list {
#         value: "m"
#         value: "n"
#       }
#     }
#   }
# }

 

一幅圖總結一下上面的代碼

 

Example 協議塊

它其實是一種 數據存儲的 格式,類似於 xml、json 等;

用上述方法實現該格式;

一個 Example 協議塊對應一個樣本,一個樣本有多種特徵,每種特徵下有多個元素,可參看上圖;

message Example{
    Features features = 1;
}
message Features{
    map<string,Features> feature = 1;
}
message Feature {
    oneof kind {
        BytesList bytes_list = 1;
        FloateList float_list = 2;
        Int64List int64_list = 3;
    }
}

TFRecord 文件就是以 Example協議塊 格式 存儲的;

 

TFRecord 文件

該類文件具有寫功能,且可以把其他類型的文件轉換成該類型文件,其實相當於先讀取其他文件,再寫入 TFRecord 文件;

該類文件也具有讀功能;

 

TFRecord 存儲

存儲分兩步:

1.建立存儲器 

2. 構造每個樣本的 Example 協議塊

 

tf.python_io.TFRecordWriter(file_name)

構造存儲器,存儲器有兩個常用方法

  • write(record):向文件中寫入一個樣本
  • close():關閉存儲器

注意:此處的 record 為一個序列化的 Example,通過 Example.SerializeToString()來實現,它的作用是將 Example 中的 map 壓縮為二進制,節約大量空間

 

示例代碼1:將 MNIST 數據集保存成 TFRecord 文件

import tensorflow as tf
import numpy as np
import input_data


# 生成整數型的屬性
def _int64_feature(value):
    return tf.train.Feature(int64_list = tf.train.Int64List(value = [value]))

# 生成字符串類型的屬性,也就是圖像的內容
def _string_feature(value):
    return tf.train.Feature(bytes_list = tf.train.BytesList(value = [value]))

# 讀取圖像數據 和一些屬性
mniset = input_data.read_data_sets('../../../data/MNIST_data',dtype=tf.uint8, one_hot=True)
images = mniset.train.images
labels = mniset.train.labels
pixels = images.shape[1]        # (55000, 784)
num_examples = mniset.train.num_examples        # 55000

file_name = 'output.tfrecords'          ### 文件名
writer = tf.python_io.TFRecordWriter(file_name)     ### 寫入器

for index in range(num_examples):
    ### 遍歷樣本
    image_raw = images[index].tostring()        ### 圖片轉成 字符型
    example = tf.train.Example(features = tf.train.Features(feature = {
        'pixel': _int64_feature(pixels),
        'label': _int64_feature(np.argmax(labels[index])),
        'image_raw': _string_feature(image_raw)
    }))
    writer.write(example.SerializeToString())       ### 寫入 TFRecord
writer.close()

 

示例代碼2:將 csv 保存成 TFRecord 文件

train_frame = pd.read_csv("../myfiles/xx3.csv")
train_labels_frame = train_frame.pop(item="label")
train_values = train_frame.values
train_labels = train_labels_frame.values
print("values shape: ", train_values.shape)     # values shape:  (2, 3)
print("labels shape:", train_labels.shape)      # labels shape: (2,)

writer = tf.python_io.TFRecordWriter("xx3.tfrecords")

for i in range(train_values.shape[0]):
    image_raw = train_values[i].tostring()
    example = tf.train.Example(
        features=tf.train.Features(
            feature={
                "image_raw": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_raw])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[train_labels[i]]))
            }
        )
    )
    writer.write(record=example.SerializeToString())
writer.close()

 

示例3:將 png 文件保存成 TFRecord 文件

# filenames = tf.train.match_filenames_once('../myfiles/*.png')
filenames = glob.iglob('..\myfiles\*.png')

writer = tf.python_io.TFRecordWriter('png.tfrecords')

for filename in filenames:
    img = Image.open(filename)
    img_raw = img.tobytes()
    label = 1
    example = tf.train.Example(
        features=tf.train.Features(
            feature={
                "image_raw": tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
            }
        )
    )
    writer.write(record=example.SerializeToString())
writer.close()

 

TFRecord 讀取

參考鏈接:https://www.cnblogs.com/nbk-zyc/p/13159986.html(案例6)、https://www.cnblogs.com/nbk-zyc/p/13168313.html

 

tf.TFRecordReader()

建立讀取器,有 read 和 close 方法

tf.parse_single_example(serialized,features=None,name= None)

解析單個 Example 協議塊

  • serialized : 標量字符串的Tensor,一個序列化的Example,文件經過文件閱讀器之後的value
  • features :字典數據,key為讀取的名字,value為FixedLenFeature
  • return : 一個鍵值對組成的字典,鍵為讀取的名字

features中的value還可以為tf.VarLenFeature(),但是這種方式用的比較少,它返回的是SparseTensor數據,這是一種只存儲非零部分的數據格式,了解即可。

tf.FixedLenFeature(shape,dtype)

  • shape : 輸入數據的形狀,一般不指定,為空列表
  • dtype : 輸入數據類型,與存儲進文件的類型要一致,類型只能是float32,int 64, string
  • return : 返回一個定長的 Tensor (即使有零的部分也存儲)

 

示例代碼

filename = 'png.tfrecords'
file_queue = tf.train.string_input_producer([filename], shuffle=True)

reader = tf.TFRecordReader()
key, value = reader.read(file_queue)

### features 的 key 必須和 寫入時 一致,數據類型也必須一致,shape 可為 空
dict_data= tf.parse_single_example(value, features={'label': tf.FixedLenFeature(shape=(1,1), dtype=tf.int64),
                                                        'image_raw': tf.FixedLenFeature(shape=(), dtype=tf.string)})
label = tf.cast(dict_data['label'], tf.int32)
img = tf.decode_raw(dict_data['image_raw'], tf.uint8)       ### 將 string、bytes 轉換成 int、float

image_tensor = tf.reshape(img, [500, 500, -1])

sess = tf.Session()
sess.run(tf.local_variables_initializer())
tf.train.start_queue_runners(sess=sess)

while 1:
    # print(sess.run(key))        # b'png.tfrecords:0'
    image = sess.run(image_tensor)
    img_PIL = Image.fromarray(image)
    img_PIL.show()

 

參考資料:

https://blog.csdn.net/chengshuhao1991/article/details/78656724

https://www.cnblogs.com/yanshw/articles/12419616.html

,

TFRecord 是 tensorflow 內置的文件格式,它是一種二進制文件,具有以下優點:

1. 統一各種輸入文件的操作

2. 更好的利用內存,方便複製和移動

3. 將二進制數據和標籤(label)存儲在同一個文件中

 

引言

在了解如下操作後進一步詳細講解TFRecord

 

tf.train.Int64List(value=list_data)

它的作用是 把 list 中每個元素轉換成 key-value 形式,

注意,輸入必須是 list,且 list 中元素類型要相同,且與 Int 保持一致;

# value = tf.constant([1, 2])     ### 這會報錯的
ss = 1               ### Int64List 對應的元素只能是 int long,其他同理
tt = 2
out1 = tf.train.Int64List(value = [ss, tt])
print(out1)
# value: 1
# value: 2

ss = [1 ,2]
out2 = tf.train.Int64List(value = ss)
print(out2)
# value: 1
# value: 2

 

同類型的 方法還有 2 個

tf.train.FloatList
tf.train.BytesList

 

tf.train.Feature(int64_list=)

它的作用是 構建 一種類型的特徵集,比如 整型

out = tf.train.Feature(int64_list=tf.train.Int64List(value=[33, 22]))
print(out)
# int64_list {
#   value: 33
#   value: 22
# }

也可以是其他類型

tf.train.Feature(float_list=tf.train.FloatList())
tf.train.Feature(bytes_list=tf.train.BytesList())

 

tf.train.Features(feature=dict_data)

它的作用是 構建 多種類型 的特徵集,可以 dict 格式表達 多種類型

ut = tf.train.Features(feature={
                            "suibian": tf.train.Feature(int64_list=tf.train.Int64List(value=[1, 2, 4])),
                            "a": tf.train.Feature(float_list=tf.train.FloatList(value=[5., 7.]))
                        })
print(out)
# feature {
#   key: "a"
#   value {
#     float_list {
#       value: 5.0
#       value: 7.0
#     }
#   }
# }
# feature {
#   key: "suibian"
#   value {
#     int64_list {
#       value: 1
#       value: 2
#       value: 4
#     }
#   }
# }

 

tf.train.Example(features=tf.train.Features())

它的作用是創建一個 樣本,Example 對應一個樣本

example = tf.train.Example(features=
                           tf.train.Features(feature={
                               'a': tf.train.Feature(int64_list=tf.train.Int64List(value=range(2))),
                               'b': tf.train.Feature(bytes_list=tf.train.BytesList(value=[b'm',b'n']))
                           }))
print(example)
# features {
#   feature {
#     key: "a"
#     value {
#       int64_list {
#         value: 0
#         value: 1
#       }
#     }
#   }
#   feature {
#     key: "b"
#     value {
#       bytes_list {
#         value: "m"
#         value: "n"
#       }
#     }
#   }
# }

 

一幅圖總結一下上面的代碼

 

Example 協議塊

它其實是一種 數據存儲的 格式,類似於 xml、json 等;

用上述方法實現該格式;

一個 Example 協議塊對應一個樣本,一個樣本有多種特徵,每種特徵下有多個元素,可參看上圖;

message Example{
    Features features = 1;
}
message Features{
    map<string,Features> feature = 1;
}
message Feature {
    oneof kind {
        BytesList bytes_list = 1;
        FloateList float_list = 2;
        Int64List int64_list = 3;
    }
}

TFRecord 文件就是以 Example協議塊 格式 存儲的;

 

TFRecord 文件

該類文件具有寫功能,且可以把其他類型的文件轉換成該類型文件,其實相當於先讀取其他文件,再寫入 TFRecord 文件;

該類文件也具有讀功能;

 

TFRecord 存儲

存儲分兩步:

1.建立存儲器 

2. 構造每個樣本的 Example 協議塊

 

tf.python_io.TFRecordWriter(file_name)

構造存儲器,存儲器有兩個常用方法

  • write(record):向文件中寫入一個樣本
  • close():關閉存儲器

注意:此處的 record 為一個序列化的 Example,通過 Example.SerializeToString()來實現,它的作用是將 Example 中的 map 壓縮為二進制,節約大量空間

 

示例代碼1:將 MNIST 數據集保存成 TFRecord 文件

import tensorflow as tf
import numpy as np
import input_data


# 生成整數型的屬性
def _int64_feature(value):
    return tf.train.Feature(int64_list = tf.train.Int64List(value = [value]))

# 生成字符串類型的屬性,也就是圖像的內容
def _string_feature(value):
    return tf.train.Feature(bytes_list = tf.train.BytesList(value = [value]))

# 讀取圖像數據 和一些屬性
mniset = input_data.read_data_sets('../../../data/MNIST_data',dtype=tf.uint8, one_hot=True)
images = mniset.train.images
labels = mniset.train.labels
pixels = images.shape[1]        # (55000, 784)
num_examples = mniset.train.num_examples        # 55000

file_name = 'output.tfrecords'          ### 文件名
writer = tf.python_io.TFRecordWriter(file_name)     ### 寫入器

for index in range(num_examples):
    ### 遍歷樣本
    image_raw = images[index].tostring()        ### 圖片轉成 字符型
    example = tf.train.Example(features = tf.train.Features(feature = {
        'pixel': _int64_feature(pixels),
        'label': _int64_feature(np.argmax(labels[index])),
        'image_raw': _string_feature(image_raw)
    }))
    writer.write(example.SerializeToString())       ### 寫入 TFRecord
writer.close()

 

示例代碼2:將 csv 保存成 TFRecord 文件

train_frame = pd.read_csv("../myfiles/xx3.csv")
train_labels_frame = train_frame.pop(item="label")
train_values = train_frame.values
train_labels = train_labels_frame.values
print("values shape: ", train_values.shape)     # values shape:  (2, 3)
print("labels shape:", train_labels.shape)      # labels shape: (2,)

writer = tf.python_io.TFRecordWriter("xx3.tfrecords")

for i in range(train_values.shape[0]):
    image_raw = train_values[i].tostring()
    example = tf.train.Example(
        features=tf.train.Features(
            feature={
                "image_raw": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_raw])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[train_labels[i]]))
            }
        )
    )
    writer.write(record=example.SerializeToString())
writer.close()

 

示例3:將 png 文件保存成 TFRecord 文件

# filenames = tf.train.match_filenames_once('../myfiles/*.png')
filenames = glob.iglob('..\myfiles\*.png')

writer = tf.python_io.TFRecordWriter('png.tfrecords')

for filename in filenames:
    img = Image.open(filename)
    img_raw = img.tobytes()
    label = 1
    example = tf.train.Example(
        features=tf.train.Features(
            feature={
                "image_raw": tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
            }
        )
    )
    writer.write(record=example.SerializeToString())
writer.close()

 

TFRecord 讀取

參考鏈接:https://www.cnblogs.com/nbk-zyc/p/13159986.html(案例6)、https://www.cnblogs.com/nbk-zyc/p/13168313.html

 

tf.TFRecordReader()

建立讀取器,有 read 和 close 方法

tf.parse_single_example(serialized,features=None,name= None)

解析單個 Example 協議塊

  • serialized : 標量字符串的Tensor,一個序列化的Example,文件經過文件閱讀器之後的value
  • features :字典數據,key為讀取的名字,value為FixedLenFeature
  • return : 一個鍵值對組成的字典,鍵為讀取的名字

features中的value還可以為tf.VarLenFeature(),但是這種方式用的比較少,它返回的是SparseTensor數據,這是一種只存儲非零部分的數據格式,了解即可。

tf.FixedLenFeature(shape,dtype)

  • shape : 輸入數據的形狀,一般不指定,為空列表
  • dtype : 輸入數據類型,與存儲進文件的類型要一致,類型只能是float32,int 64, string
  • return : 返回一個定長的 Tensor (即使有零的部分也存儲)

 

示例代碼

filename = 'png.tfrecords'
file_queue = tf.train.string_input_producer([filename], shuffle=True)

reader = tf.TFRecordReader()
key, value = reader.read(file_queue)

### features 的 key 必須和 寫入時 一致,數據類型也必須一致,shape 可為 空
dict_data= tf.parse_single_example(value, features={'label': tf.FixedLenFeature(shape=(1,1), dtype=tf.int64),
                                                        'image_raw': tf.FixedLenFeature(shape=(), dtype=tf.string)})
label = tf.cast(dict_data['label'], tf.int32)
img = tf.decode_raw(dict_data['image_raw'], tf.uint8)       ### 將 string、bytes 轉換成 int、float

image_tensor = tf.reshape(img, [500, 500, -1])

sess = tf.Session()
sess.run(tf.local_variables_initializer())
tf.train.start_queue_runners(sess=sess)

while 1:
    # print(sess.run(key))        # b'png.tfrecords:0'
    image = sess.run(image_tensor)
    img_PIL = Image.fromarray(image)
    img_PIL.show()

 

參考資料:

https://blog.csdn.net/chengshuhao1991/article/details/78656724

https://www.cnblogs.com/yanshw/articles/12419616.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

聚甘新

為什麼Web開發人員在2020年不用最新的CSS功能

轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。

原文出處:https://dzone.com/articles/why-masses-are-not-using-latest-css-features-in-20

 

儘管CSS每年都會發布全新的特性,但實際上這些新功能很少會被web開發人員實際在生產項目中使用到,甚至去了解它們的動力也不會比去多完成幾個需求更多。那究竟是什麼原因導致的呢? 

1.使用最新特性不是優先事項

在一個新項目的初期階段,它用到的可能只是幾條CSS規則,但隨着項目的持續更新和迭代,項目中使用到的規則就會變得越來越複雜,CSS也會越來越複雜尺寸也會隨之不斷膨脹。因此,作為項目優化的第一要務,作為資源的CSS需要盡可能的精簡和減少複雜度,第一是為了便於更好地理解和維護,第二也是為了加載更為高效。那麼,更實用且可投入生產環境的一些CSS特性會被高頻使用,其他的特性則會被暫時擱置一旁。

並且,在一般情況下,樣式和品牌在一段時間內都會相對固定,完成任務的需求要比使用最新CSS的特性要更緊迫。 

 

 

(圖片來源於網絡)

 

2.預算限制

預算成本是影響了所有項目的主要因素。它在開發階段會高度影響事項的優先級。集成新的CSS功能需要時間,而開發團隊來說,增加的這部分時間成本會影響到項目的整體進度。因此,開發進度會重視在優化其他功能(而不是CSS功能)時花費的時間成本。

另外,引入了最新的CSS特性,還可能會使開發團隊把一部分精力放在解決瀏覽器兼容性問題上。這點和JavaScript不同,JavaScript有Babel來完成編譯,而CSS沒有提供類似功能。

 

 

 (圖片來源於網絡)

 

3.社區發展還未跟上

JavaScript每隔一段時間舉行一次會議。同樣,Vue和React也會為了幫助開發人員跟上社區的步伐而定期舉行會議。但是,對於CSS而言,它們根本沒有這樣的活動!因此,開發人員很難掌握其功能和路線圖。他們應該如何保持對新功能發展趨勢的了解? 沒有版本發布說明,也沒有定期的發布會,這根本不能點燃社區用戶的學習激情。 

對普通用戶而言,既然舊的技術已經滿足了需求,那麼又何必那麼麻煩閱讀文檔學習新的功能呢?

和框架和其他編程語言不同,CSS沒有針對安全問題的補丁程序。他只是一套標準,反正大多數客戶只需要關心網站看起來UI差不多就行了。

4.很難提升簡歷的含金量

即使你在掌握CSS方面付出了很多的努力,對CSS的新特性也了如指掌,但你也很難向你的客戶或老闆證明這一點,因為類似像這樣“熟練掌握CSS3以外的CSS特性”對他人而講是沒有意義的,因為它不是CSS3。在CSS開發領域,CSS3的出現是很有意義的,因為它完成了前端領域的統一:

  • Web開發人員提升了技能
  • 加速瀏覽器廠商統一支持了全新的CSS標準
  • 企業的技術棧更新

巨大的需求帶來了巨大的機會。除了大量的書籍、課程和視頻來幫助人們了解CSS3外,還催生了全新的布局模型,如Flexbox和Grid,儘管它們不是CSS3的一部分。

但這裏我們指的是CSS3外的特性,它們本身除了認可程度很低外,對開發團隊來講也是個相對不熟悉的東西,因此,開發團隊很難會把時間花在對市場沒有意義的事情上,客戶也不會關心你到底用不用新的技術。

5.缺乏時間

編寫CSS的主要目的是使你的網站的表現內容的形式更美觀及易於理解。CSS通過控制兩類事物來幫助開發人員去實現這個目標:布局和設計。布局(Layout)負責元素列和行排布,而設計(design)指顏色、字體、間距、動畫和邊框等基礎外觀。

但目前,舊的特性已經能處理的很好了,為什麼要花更多時間去使用新特性去替代已經很好的形式呢?

總結

CSS發布周期沒有固定的周期和計劃,導致一切都來的很突然 ,另外舊的CSS特性已經能很好的完成日常工作了,這讓很多Web開發人員沒有特別的動力去升級它們。

另外,新的特性知名度也不高,對最終用戶的吸引力也不足,很難從需求層面驅動使用。所以這就是為什麼都2020年了,CSS的新特性仍然使用的人較少的原因。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司“嚨底家”!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

福斯汽車、SMA Solar傳聯手發展電動車

福斯汽車(Volkswagen)深陷柴油車排氣造假案使得品牌形象受重創,為了早點擺脫「柴油門」陰影,轉移消費者的眼光,福斯近來大力推動電動車,雄心勃勃的發展電動車的同時,傳出找來德國逆變器大廠SMA Solar 結盟。

SMA Solar 成立於1981 年,公司名來自「系統、測量、系統工程」(System, Mess and Anlagentechnik)德文簡寫,為全球最大逆變器廠,逆變器應用於直流電交流電轉換,不僅應用於太陽能,也應用於電池能源儲存系統,SMA Solar 市場地位穩固,特斯拉(Tesla)於2016 年5 月宣布Powerwall 將進行小規模規格提升,其重點之一就是改為可支援SMA Solar 產品。

福斯在「柴油門」事件後,積極將事業目標轉向電動車,以便洗刷受到作假事件影響的聲譽,投注數十億歐元資金,大力發展零碳排放的純電動車以及隨選共乘服務,計劃在2025 年以前推出30 款電動車,攻佔四分之一的全球電動車市場。

但是分析師指出,福斯在2015 年只不過出貨6.7 萬輛電動車與油電混合車,以這樣的市佔與進度,要在2025 年攻佔四分之一電動車市場的目標可能很難達成,尤其是競爭對手早已起步,為了追上豐田(Toyota)與雷諾(Renault)日產(Nissan)聯盟,福斯需要找尋強大的合作夥伴。

德國地方日報《黑森下薩克森大眾報》(Hessische Niedersaechsische Allgemeine)於2016 年8 月初報導,福斯尋求能源儲存與綠能大廠SMA Solar 的合作。

福斯的電動車廠表示,SMA Solar 在靜態能源儲存領域相當強勢,對福斯來說,尋求正確的合作夥伴,將是發展電動車的重要關鍵。不過,SMA Solar 目前已經與賓士製造商戴姆勒(Daimler)以及特斯拉在能源儲存方面合作,目前對於福斯的可能合作消息不表示意見。

(本文授權自《》──〈〉。照片來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司“嚨底家”!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

.Net微服務實戰之DevOps篇,.Net微服務實戰之技術選型篇,.Net微服務實戰之技術架構分層篇

技術只是基礎

  該系列的兩篇文章《.Net微服務實戰之技術選型篇》和《.Net微服務實戰之技術架構分層篇》都是以技術角度出發描述微服務架構的實施。

  如果技術選型篇敘述的是工具,那麼架構分層篇講的就是技巧,而本篇要討論的就是原則。一直以來我會給身邊向我探討問題的人灌輸一種理念,沒有什麼技術銀彈,因為我們做的是軟件工程,提供的是問題相應的解決方案,不同類型問題的解決方案是存在着本質上的差異。

  繼續提供之前的源碼:https://github.com/SkyChenSky/Sikiro

PS:該篇文章與.Net無關,其實主要是沿用前面兩篇文章的命名,此外我認為DevOps不是簡單的工具使用,應從軟件工程角度進行出發。

什麼才是優秀的架構設計?

  曾經有好幾個同行問過我同一個問題:什麼才是優秀的架構設計?我一直信奉着兩句話一個定律

  • 架構服務於業務,技術服務於架構 
  • 康威定律(簡單理解成組織架構的設計等同於系統架構的設計)

  架構設計其實就是一種方案取捨,在有限資源里(包括但不限人力、時間)能讓團隊順利的實施技術,同時滿足業務規模的需要,我認為可以稱之為優秀的架構設計,簡單來說兩個字合適

 

架構核心要素

  核心的主要5大:性能、可用性、伸縮性、擴展性、安全性。 

  而我們所討論的微服務,選擇了擴展性,犧牲了可用性、性能,擴展性的目的就是為了快速響應需求變化降低系統耦合度提高系統模塊的復用度。而微服務的調用是通過跨進程的網絡通信的,跟進程內方法調用比無疑是慢了一個單位;原本單服務99.99%高可用,假如現在三個服務就是99.99%*99.99%*99.99%=99.97%。

  當然我們可以在基於微服務的通過引入其他技術提高可用性、伸縮性和安全,但是確保無疑是犧牲了性能,除了性能還會在團隊開發效率與運維複雜度上會受到影響。由此可見,沒有萬能技術手段,而架構其實在取捨。

  引入一種技術必定帶新的技術問題這是個必然結果,剛提到團隊開發效率運維複雜度會受到影響,那是否有辦法緩解甚至解決並提高呢?既然涉及到團隊、流程這些關鍵字那麼就應該向軟件工程方向尋找方案,合適架構實施還需要合適的開發模式進行支撐的,而風靡全球的DevOps就是不二之選。

軟件工程

   在行業盛傳的一條公式:軟件 = 軟件工程 + 程序,可想而知軟件工程的佔據多麼重要的比重。那麼什麼是軟件工程?百度是這麼解釋的:

  軟件工程是研究和應用如何以系統性的、規範化的、可定量的過程化方法去開發和維護軟件,以及如何把經過時間考驗而證明正確的管理技術和當前能夠得到的最好的技術方法結合起來的學科。它涉及到程序設計語言、數據庫、軟件開發工具、系統平台、標準、設計模式等方面。

  我自己重新總結了一個軟件工程的通俗描述,通過多人協作、有目標、有步驟、有計劃的並使用科學方法論指導開發與維護程序的這個過程。也可以用一條公式表達:軟件工程 = 工具 + 流程 + 模式。

 

軟件危機

  軟件工程的出現目的是為了解決軟件危機的。軟件危機其實是當時落後的軟件生產方式無法滿足迅速增長的計算機軟件需求,從而導致軟件開發與維護過程中出現一列的嚴重問題的現象。那麼三次軟件危機是什麼呢?我整理了個表格(詳細可以自行百度閱讀)

名稱 時間 原因 解決方案
第一次軟件危機

20世紀60年代—70年代

使用機器語言或者彙編語言在特定的機器上進行軟件的設計與編寫,引出的“抽象性”和“可移植性”的問題 高級的編程語言+瀑布開發模式
第二次軟件危機

20世紀80年代—90年代

軟件複雜性進一步升級,需要更好更好的“可組合性”(Composability)、“可延展性”(Malleability)以及“可維護性”(Maintainability) 面向對象編程語言+設計模式
第三次軟件危機 2005年至今 軟件的發展速度已經遠超於硬件的發展,體現於需求複雜度、技術複雜度、團隊協作 更好的工具、開發模式、與協作流程

 

   由上可見,軟件的快速發展直接促使了軟件工程上的進步,新的工具、新的開發與設計模式,新的協作流程也隨之而生。

開發模式的發展

  我工作多年經歷了多家公司,所經歷的有三種開發模式,瀑布、敏捷、DevOps。那麼這三種主流的開發模式也對應着三個發展階段:

瀑布開發模式

  瀑布開發模式是在第一次軟件危機1970時Winston Royce博士提出來。其思想是把項目過程劃分為主要的六個階段:需求收集、需求分析、軟件設計、程序編碼、軟件測試、運行維護。團隊劃分也通過崗位職責進行劃分:產品團隊、開發團隊、測試團隊、運維團隊。到目前為止該開發模式仍然用到做項目制的開發團隊。

  那麼其優點與劣勢也很明顯,優點是計劃明確,職責清晰,按部就班的完成就好。缺點是周期容易拖得太長,不容易調整變更,每個人只為自己職責範圍內的負責,跨部門溝通成本大(這就是為什麼我在圖裡畫了兩堵牆的原因)。我自己呆過一個瀑布模式的團隊,在項目立項后就會被項目經理調動資源成為團隊,而開發人員只會在這一次批次負責編碼與修改測試反饋的問題,基本上上線后的問題跟你無關(除非緊急嚴重的),其他的BUG也許是下一個批次的另外一個開發人員幫你填。

 敏捷開發模式

  準確的說敏捷開發是一種價值觀和原則的體現,2001年17位IT大佬想把瀑布發模式這種重量級的開發過程替換成一種更加輕量級,可惜大家都沒有達成統一意見因此把各自都認同的觀念整理出來成為敏捷宣言。

  敏捷開發其實把產品、開發、測試三種崗位職責的人緊密的聯繫了起來,由原來長周期的大目標拆解成了一個個短周期的小目標。他之所以快,不是因為寫代碼快了,而是節省了很多不必要的前置條件與返工,同時小步快跑的交付也可以提高團隊的士氣,一個長周期項目那枯燥、乏味、痛苦的過程,誰試誰知道。

  舉個例子,大家都是為公司的同個產品努力,沒有什麼合同談判可言,只要需求要求相互了解清楚並且可行就可以開幹了。寫詳細設計文檔的時間,還不如花時間多溝通下需求的核心點,想辦法設計得更容易滿足需求。短周期的交付后,產品與客戶就可以及時的查看交付效果並相應的優化與調整。(快速響應並不代表隨時隨地接受變更響應,可以統一歸到下一個迭代周期,我不贊同拍拍腦袋的變更,自己都沒清楚的功能怎麼說服客戶使用?

  

  敏捷開發的最大好處之一就是短周期的持續交付,這樣方式能在現階段的互聯網行業得到更快速的響應與市場的搶佔,同時能很好的進行技術改進與試錯。但是這種”野蠻的“方式會讓開發團隊與運維團隊形成一條鴻溝,而鴻溝的形成主要原因是運維團隊希望軟件的運作是可靠的,所以他們對資源的變動、新技術的使用尤為的小心、謹慎。

  我曾經呆過一個敏捷開發團隊,生產出了問題運維團隊會自行去修改配置,當然會越改越錯了,而且一天發布次數多了,就會起爭執。

DevOps

  DevOps可以看過是敏捷的擴展與延申,它的出現就是為了解決開發團隊與運維團隊的那條鴻溝,只要存在人工處理的方式擔心的問題總會出現,同一段程序無論執行多少次相同輸入的輸出總是一致的,但是人的處理卻不能保證,那麼使用自動化改善協作的過程,鴻溝自然就跨越了,。那麼開發團隊與運維團隊就可以為相同的目標與方向而努力。而組織架構也將演變成如下:

  

  從上圖也與開頭的康威定律做了一個很好的呼應。

 我是如何實施DevOps的?

 

技術

  這個角度是大家最樂意去關注的,在我們團隊主要使用了以下技術,腳本什麼的我就不花時間貼出來了,在我看來工具的使用,只要花點時間就能解決。

類型 名稱
持續集成/持續交付 Jenkins
源代碼管理 Gitlab
雲平台 阿里雲
軟件包管理器 私有Nuget
代碼檢查 Reshaper
容器化 Docker
分佈式鏈路跟蹤 SkyWalking
日誌系統 ES+Filebeat+kibana
系統監控 Prometheus

  原本代碼檢查想引入SonarQube代替人工檢查+Reshaper,可惜於服務器資源不足。

  對於一般的團隊,我建議優先從Gitlab+Jenkins搭建好完成CI/CD,其次把日誌系統給完善起來,這兩者完成得越早,給團隊帶來的收益就越高,後續才會有更多的時間來完善整套技術體系,這是一個良性的循環

  人延申出的就是團隊與文化,經過上面的講解大家都意識到軟件工程就是一樣多人協作的工作,只有團隊目標一致,共同負責承擔團隊的項目,願意一同與項目成長才能很好的實施DevOps。就像多匹馬拉車一樣,只有它們都有共同的目標的時候才能快速拉車到目的,如果他們一匹向東一匹西,只會讓馬車無法前行甚至四分五裂。

  在我的團隊,因為在招聘人員的時候已經進行過了篩選,所以在合作上非常的順利,當然我也經常在例會和業餘的時候都會給大家傳達思想,讓團隊成員真正的從實際意義上去理解現在的做法。

  對於已經成型的團隊來說如何去落地呢?無非三種,激勵、考核和逐步試行。如果有條件的公司可以設置獎金激勵,如果有績效考核的可以將DevOps實施納入考核目標,如果兩者都沒的,那就選取團隊里願意改變的同事進行試行,使用過後都說好的那麼更會有說服力。 

流程 

   為了落實了文化的改進與技術的使用的這個過程,我們需要科學的、有步驟、有計劃的方式完成這項工作,並且可以讓這套標準化的方式可以重複使用到其他項目上。

  在我的團隊是有產品、前端開發,後端開發、測試、運維組成的。我採用了原型模式+DevOps模式:

  •   產品人員會優先使用Axure RP工具把需求整理產出原型並與需求方確認。
  •   產品確認好的原型就是我們技術的輸入,技術拿到需求後會做一次需求評審,主要是排查需求疑惑和確認需求目標。
  •   需求明確后,由我使用Visual Project任務拆解與排期,任務會建立在我們的項目管理系統Redmine上,如果任務周期過程,我會拆分成多個可交付的短周期,一般會控制在2個星期內。
  •   接到任務后,大家就跟根據自己的任務使用PowerDesigner數據庫設計(早期是由我獨裁設計,後期團隊發展壯大了,就由業務負責人各自設計),在這個階段,如果有新的服務與新的工具庫需要部署,我就會正面與運維溝通讓他把自動化給完成。
  •   因為我們是前後端分離的,所以我們使用了Swagger減少了寫接口文檔的時間,所有任務是否完成以前端是否對接好接口為主導,前端對接好后,就會在Redmine修改自己的任務狀態並新建一個測試任務給到測試。
  •   測試會根據自己寫好的測試用例,進行對完成的任務進行場景測試,如果有BUG會在Redmine提給相應的人進行修改。一般會先由前端人員排查是否是他的交互上的BUG,如果確認是數據問題那麼就會轉給後端開發,開發人員定位BUG時,可以通過我們的SkyWalking和Kibana聯合定位問題,定位問題時間一般都在2-10分鐘。
  •   代碼合併到測試分支后就會通過Jenkins發布到測試環境,生產環境的發布是合併到生產環境後手動確認發布的。

  除此之外,每周一會有一個例會內容不限工作,也可以分享周末去哪裡娛樂了。在該迭代周期快到結束的2-3天會開一個進度會議,看看大家完成情況。因為公司沒有下午茶,所以我們自己通過玩搶紅包領到最大的兩個的請吃下午茶,最少一星期一次。

 結束

  該篇到這裏就分享結束了,也是該系列的最後一篇,我曾經認為技術與管理必須二選一,自從我成為了一個技術與團隊的負責人後,終於讓我認識到,一個優秀的技術思想還是需要一些管理手段才能很好的實施,而我們的技術管理無非就是軟件工程

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

聚甘新

cute-cnblogs 自定義博客園樣式美化二期來啦~

cute-cnblogs 自定義博客園樣式美化二期來啦~

說明

cute-cnblogs 可愛的博客園樣式美化、自定義博客園樣式 二期樣式已經編寫完畢了,如果說 一期樣式 給人的感覺是簡潔清爽的小嬰兒的話,那麼 二期樣式 就是一個有自己小個性(花樣)的小朋友了~

與一期一樣,需要文件的可以來 github ,喜歡我寫的樣式可以幫我點個 star 喔 ღゝ◡╹)ノ

(PS:有什麼問題也可以留言到 github issues 中喔~)

好了,讓我們擼起袖子開始更換二期樣式吧~

博客示例

ღゝ◡╹)ノ 麋鹿魯喲的博客園

介紹

可愛的博客園樣式美化、自定義博客園樣式 ღゝ◡╹)ノ

  • 本樣式以簡約可愛為核心,美化博客園的显示效果,增加自定義導航;
  • 基於博客園主題“Custom”進行的樣式修改;
  • 閱讀目錄導航;
  • 支持響應式;

功能

一期功能

  • 頂部背景點點動效隨鼠標而動
  • 導航欄自定義
  • 頁面詩意詩句模塊
  • 看板娘-貓(ฅ´ω`ฅ) (自行決定是否添加,因為這個有些影響加載速度)
  • 音樂-網易雲 (自行決定是否添加)
  • 上弔的貓(PS:回到頂部)(已用其餘按鈕代替此功能)
  • 底部跳動的魚<・)))><<
  • 點擊頁面跳動的小豆子及可愛的文字(自行決定是否添加可愛文字的點擊)
  • 評論增加OωO聊天表情
  • 頁面不同的導航背景及人物背景

二期增加功能

  • 側邊欄显示
  • 側邊欄公告欄及個人信息显示
  • 閱讀目錄(標題 h1 > h2 > h3 寫了三級目錄)
  • ️ 主題皮膚切換(淺白、暗黑、閱讀)
  • 仿主播點贊功能點擊推薦
  • ️ 讚賞模塊功能

模版選定

博客皮膚選定: 博客園 Custom 標準模板(與一期不同喔)

使用方法

基本操作

請按照順序進行操作喔~

  • 首先記得申請JS權限

  • 其次博客皮膚選擇 Custom

  • 在此需要獲取數據(不然點擊頭像的關注會失敗)
    找一個沒有登陸的瀏覽器訪問自己的博客園,F12彈出窗口,找到 +加關注,複製follow括號里的內容,暫且先存在一個地方

  • 勾選禁用模板默認CSS

  • 創建一個新隨筆(這裏使用選項中的TinyMCE(推薦)來編寫的) —— “友鏈”;(注意,此處已與一期不同)
  • 點擊 “編輯 HTML 源代碼” 插入以下代碼,修改自己的文本內容后,點擊更新;
  • 只勾選 高級選項中的 “發布”、“允許評論”;
<p style="text-align: center;">歡迎來到我的友鏈小屋</p>
<div class="friendsbox">
<div id="app">
<h6 style="text-align: center; color: #2daebf;">展示本站所有友情站點,排列不分先後,均勻打亂算法隨機渲染的喔!</h6>
<div class="unionbox-box" style="display: flex; flex-wrap: wrap; justify-content: space-between; margin-bottom: 1.5rem; margin-top: 1.5rem;">&nbsp;</div>
<hr style="position: relative; margin: 1rem 0; border: 1px dashed #9eabb3; width: calc(100%); height: 0;" />
<h2 style="text-align: center;">友鏈信息</h2>
<h5 style="text-align: left; line-height: 30px;">博客名稱:<a href="javascript:void(0)">麋鹿魯喲</a><br />博客網址:<a href="javascript:void(0)">https://www.cnblogs.com/miluluyo/</a><br />博客頭像:<a href="javascript:void(0)">https://pic.cnblogs.com/avatar/1273193/20190806180831.png</a><br />博客介紹:<a href="javascript:void(0)">大道至簡,知易行難。</a></h5>
<h2 style="text-align: center;">join us</h2>
<h5 style="text-align: center; color: #2daebf;">如需友鏈,請添加微信(s978761)告知,格式如下</h5>
<table class="table friendstable" style="margin: 0 auto;">
<tbody>
<tr><th><strong>字段</strong></th><th><strong>字義</strong></th></tr>

</tbody>

</table>

</div>

</div>

  • 最後分別複製以下區域代碼,並根據參數更改數據(PS:路徑可進行更改也可不更改,我覺得更改后速度會快一點,自行down文件上傳到博客園文件中,並更改引入路徑,文件都在 github 中,喜歡記得給我個star喔~)

頁面定製CSS代碼

複製 https://blog-static.cnblogs.com/files/miluluyo/cute-cnblogs2.css 的文件內容放到 頁面定製CSS代碼 區域

博客側邊欄公告

<link href="https://blog-static.cnblogs.com/files/miluluyo/tippy.min.css" rel="stylesheet">
<script src="https://blog-static.cnblogs.com/files/miluluyo/jquery2.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/tippy.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/milusidebar.js"></script>

<script>
milusidebar({
	'names' : '麋鹿魯喲',/*你的博客園名吶*/
	'notice' : '<b>溫馨提示</b><span><a href="https://github.com/miluluyo/cute-cnblogs" target="_black">cute-cnblogs</a> &nbsp;樣式已開源</span><b style="margin-top: 3px;"><a style="font-size:10px" href="https://www.cnblogs.com/IsAlpaca/" target="_black">查看一期樣式</a></b>',/*裏面文字自己可以更改喔*/
	'headerUrl' : 'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519075219notice5.png',/*這個是公告欄的背景圖啦,我覺得這個可愛,如果你有更好看的可以自行更改喔*/
	'follow' : 'a1e76459-101d-47af-a8b6-08d523685c8c', /*還記的開始讓你複製follow括號里的內容嗎,對,就放到這裏就好啦*/
	'sidebarInfo' : [[
	      {'icon':'#icon-github1','url':'https://github.com/miluluyo','title':'github'},
	      {'icon':'#icon-weixin','url':'','title':'微信','classname':'popper_weixin','click':false},
	      {'icon':'#icon-QQ','url':'http://wpa.qq.com/msgrd?v=3&uin=978761587&site=qq&menu=yes','title':'QQ'},
	      {'icon':'#icon-juejin','url':'https://juejin.im/user/5d18adce5188256e98090e33','title':'掘金'}
	  ],[
	      {'icon':'#icon-weibobangding','url':'https://www.weibo.com/6001406082/profile?topnav=1&wvr=6','title':'微博'},
	      {'icon':'#icon-csdn','url':'https://blog.csdn.net/qq_39394518','title':'CSDN'},
	      {'icon':'#icon-bilibili','url':'https://space.bilibili.com/100007925','title':'bilibili'},
	      {'icon':'#icon-yuquemianlogo','url':'https://www.yuque.com/miluluyo','title':'語雀'}
	]],/*這個模塊是個人信息內那些小圖標們,別忘記更改喔,具體參數,可以參考下面的表格喔*/
	'signature':'靡不有初  鮮克有終',/*來一句你自己喜歡的句子吧*/
	'popper_weixin':'<div class="popper_box"><p><b>很高興認識你鴨~  (づ。◕ᴗᴗ◕。)づ</b> </p><div class="popper_box_con"><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200614064005qrcode.jpg" alt="">公眾號:麋鹿魯喲</div><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1493340/t_wxh.jpg" alt="">微信號:s978761</div></div><p>(加我記得備註 博客園 喔)</div>',/*這裡是微信圖標的彈窗內容,可以自行更改內容喔*/
	'portrait':'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200515061851tx.jpg'
})/*這個是頭像圖片喔,你可以上傳到相冊里,然後F12獲取,或者使用博客園的那個鏈接也可以的撒~*/
</script>

參數說明

名稱 類型 默認值/實例 描述
names 字符串 ‘麋鹿魯喲’ 博客園名稱
notice 字符串 ‘<b>溫馨提示</b><span><a href=”https://github.com/miluluyo/cute-cnblogs” target=”_black”>cute-cnblogs</a>  樣式已開源</span><b style=”margin-top: 3px;”><a style=”font-size:10px” href=”https://www.cnblogs.com/IsAlpaca/” target=”_black”>查看一期樣式</a></b>’ 公告內容
headerUrl 字符串 ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519075219notice5.png’ 公告欄的背景圖
follow 字符串 ‘a1e76459-101d-47af-a8b6-08d523685c8c’ 複製follow括號里的內容,這是關注的那個碼
sidebarInfo 數組 [[ {‘icon’:’#icon-github1′,’url’:’https://github.com/miluluyo’,’title’:’github’}, {‘icon’:’#icon-weixin’,’url’:”,’title’:’微信’,’classname’:’popper_weixin’,’click’:false}, {‘icon’:’#icon-QQ’,’url’:’http://wpa.qq.com/msgrd?v=3&uin=978761587&site=qq&menu=yes’,’title’:’QQ’}, {‘icon’:’#icon-juejin’,’url’:’https://juejin.im/user/5d18adce5188256e98090e33′,’title’:’掘金’} ],[ {‘icon’:’#icon-weibobangding’,’url’:’https://www.weibo.com/6001406082/profile?topnav=1&wvr=6′,’title’:’微博’}, {‘icon’:’#icon-csdn’,’url’:’https://blog.csdn.net/qq_39394518′,’title’:’CSDN’}, {‘icon’:’#icon-bilibili’,’url’:’https://space.bilibili.com/100007925′,’title’:’bilibili’}, {‘icon’:’#icon-yuquemianlogo’,’url’:’https://www.yuque.com/miluluyo’,’title’:’語雀’} ]] 個人信息內那些小圖標們
icon 圖標
url 跳轉鏈接
title 提示名字
classname 要添加的class名
click 是否允許點擊跳轉
本框架有擴展的icon,文件在 github 中的 icon 文件夾內,可以下載去查看
signature 字符串 ‘靡不有初 鮮克有終’ 個人信息簽名 (寫一句喜歡的話吧)
popper_weixin 字符串 ‘< div class=”popper_box”>< p>< b>很高興認識你鴨~ (づ。◕ᴗᴗ◕。)づ< /b> < /p>< div class=”popper_box_con”>< div class=”popper_box_con_li”>< img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200614064005qrcode.jpg” alt=””>公眾號:麋鹿魯喲< /div>< div class=”popper_box_con_li”>< img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1493340/t_wxh.jpg” alt=””>微信號:s978761< /div>< /div>< p>(加我記得備註 博客園 喔)< /div>’ 微信焦點彈窗,內容可自行更改,可以放一些公眾號啊啥的~
portrait 字符串 ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200515061851tx.jpg’ 頭像圖片路徑

頁首Html代碼

  <div id="set_btn_box">
    <div class="set_btn fly_top fadeIn animated">
        <svg class="icon" aria-hidden="true"><use xlink:href="#icon-zhiding"></use></svg>
    </div>
    <div class="set_btn article_icon_btn catalogue_btn">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-dagang"></use></svg>
    </div>
    <div class="set_btn article_icon_btn comment">
        <a href="#comment_form_container"><svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-linedesign-01"></use></svg></a>
    </div>
    <div class="set_btn skin_btn">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-pifu"></use></svg>
    </div>
    <div class="set_btn gratuity">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-dashang"></use></svg>
    </div>
    <div class="set_btn article_icon_btn artice_recommend">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-tuijian2"></use></svg>
    </div>
     <canvas id="thumsCanvas" width="200" height="400" style="width:100px;height:200px"></canvas>
    <div class="set_btn catalogue">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-cebianlan-"></use></svg>
    </div>
</div>
<script src='https://blog-static.cnblogs.com/files/miluluyo/canvas2.js'></script>
<!--
<link href="//files.cnblogs.com/files/linianhui/lnh.cnblogs.css" rel="stylesheet"/>-->

頁腳Html代碼

  <style id="ceshicss">
@media (max-width: 767px){
#set_btn_box {width: 100vw;left: 0;right: 0;bottom: 0;background: hsla(0,0%,100%,.6);height: 49px;display: flex;justify-content: space-between;align-items: center;padding: 12px 40px;border-top: 1px solid #e8e8e8;box-sizing: border-box;}
.set_btn {margin-top: 0;}
.set_btn.fly_top.fadeIn.animated {position: absolute;right: 10px;bottom: 60px;}
.container{bottom:50px}}
#mainContent{width:90%}
</style>
<link href="https://blog-static.cnblogs.com/files/miluluyo/tippy.min.css" rel="stylesheet">
<script src="https://unpkg.com/@popperjs/core@2.4.2/dist/umd/popper.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/tippy.js"></script>
<link rel='stylesheet' href='https://cdn.bootcss.com/animate.css/3.7.2/animate.min.css'>
<script src="https://at.alicdn.com/t/font_1825850_klax1ao4o6.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/three.min.js"></script>
<script src='https://blog-static.cnblogs.com/files/miluluyo/star.js'></script>
<link rel="stylesheet" href="https://blog-static.cnblogs.com/files/miluluyo/OwO.min.css" />
<script src="https://blog-static.cnblogs.com/files/miluluyo/OwO2.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/cute-cnblogs2.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/monitoring2.js"></script>

<script>

miluframe({
  Youself:'https://www.cnblogs.com/miluluyo/', /*個人的博客園鏈接*/
  /*博客園導航信息*/
    custom:[{
      name:'首頁',
      link:'https://www.cnblogs.com/miluluyo/',
      istarget:false
    },{
      name:'聯繫',
      link:'https://msg.cnblogs.com/send/%E9%BA%8B%E9%B9%BF%E9%B2%81%E5%93%9F',
      istarget:true
    },{
      name:'技能樹',
      link:'https://miluluyo.github.io/',
      istarget:true
    },{
      name:'留言板',
      link:'https://www.cnblogs.com/miluluyo/p/11578505.html',
      istarget:false
    },{
      name:'相冊',
      link:'https://www.cnblogs.com/miluluyo/gallery.html',
      istarget:false
    },{
      name:'友鏈',
      link:'https://www.cnblogs.com/miluluyo/p/11633791.html',
      istarget:false
    },{
      name:'維護',
      link:'https://www.cnblogs.com/miluluyo/p/12092009.html',
      istarget:false
    },{
      name:'投喂',
      link:'https://www.cnblogs.com/miluluyo/p/gratuity.html',
      istarget:false
    },{
      name:'管理',
      link:'https://i.cnblogs.com/',
      istarget:true
    }],
    /*向別人展示自己的友鏈信息*/
    resume:{
        "name":"麋鹿魯喲",
        "link":"https://www.cnblogs.com/miluluyo/",
        "headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg",
        "introduction":"大道至簡,知易行難。"
    },
    /*友鏈信息*/
    unionbox:[{
        "name":"麋鹿魯喲",
        "introduction":"生活是沒有標準答案的。",
        "url":"https://www.cnblogs.com/miluluyo",
        "headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg"
      },{
        "name":"麋鹿魯喲的技能樹",
        "introduction":"大道至簡,知易行難。",
        "url":"https://miluluyo.github.io/",
        "headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg"
      }],
    /*友鏈表格頭信息,這個可以忽略*/
    details:[{
        field: 'name',
        literal: '昵稱',
      },{
        field: 'introduction',
        literal: '標語',
      },{
        field: 'url',
        literal: '鏈接地址',
      },{
        field: 'headurl',
        literal: '頭像地址',
      }],
    /*瀏覽器頂部小圖標*/
    logoimg:'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519070633f12.png',
    /*文章頁面標題前的圖標,此處圖標有擴展,下面會提到圖標*/
    cuteicon:['icon-caomei','icon-boluo','icon-huolongguo','icon-chengzi','icon-hamigua','icon-lizhi','icon-mangguo','icon-liulian','icon-lizi','icon-lanmei','icon-longyan','icon-shanzhu','icon-pingguo','icon-mihoutao','icon-niuyouguo','icon-xigua','icon-putao','icon-xiangjiao','icon-ningmeng','icon-yingtao','icon-taozi','icon-shiliu','icon-ximei','icon-shizi'],
    /*讚賞,若true則显示此按鈕,false則不显示*/
    isGratuity:true,
    /*讚賞按鈕焦點显示讚賞內容,內容可自行更改*/
    gratuity:'<div class="popper_box"><p><b>要請我喝奶茶嗎  (づ。◕ᴗᴗ◕。)づ</b> </p><div class="popper_box_con"><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053817wx.png" alt="">微信掃碼</div><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053827zfb.png" >支付寶掃碼</div></div><p><b>留下一句你覺得很勵志與美的話給我吧~</b>&nbsp;&nbsp;<b><a href="https://www.cnblogs.com/miluluyo/p/12930946.html">GO</a></b></div>'
})
</script>
<!-- 點贊 -->
<canvas width="1777" height="841" style="position: fixed; left: 0px; top: 0px; z-index: 2147483647; pointer-events: none;"></canvas><script src="https://blog-static.cnblogs.com/files/miluluyo/mouse-click.js"></script>

<!-- 以下內容是否添加你隨意 -->

<script>
  /*在文章頁面添加古詩詞*/
  $("#navigator").after('<div class="poem-wrap"><div class="poem-border poem-left"></div><div class="poem-border poem-right"></div><h1>念兩句詩</h1><div id="poem_sentence"></div><div id="poem_info"></div></div>')
</script>
<script src="https://sdk.jinrishici.com/v2/browser/jinrishici.js" charset="utf-8"></script>
<script type="text/javascript">
  jinrishici.load(function(result) {
    var sentence = document.querySelector("#poem_sentence")
    var info = document.querySelector("#poem_info")
    sentence.innerHTML = result.data.content
    info.innerHTML = '【' + result.data.origin.dynasty + '】' + result.data.origin.author + '《' + result.data.origin.title + '》'
  });
</script>

<script type="text/javascript">
/* 鼠標特效,我覺得太花哨了就註釋了,喜歡的自己打開註釋就可以 */
/*var a_idx = 0;
jQuery(document).ready(function($) {
    $("body").click(function(e) {
        var a = new Array("去活出你自己。","今天的好計劃勝過明天的完美計劃。","不要輕言放棄,否則對不起自己。","緊要關頭不放棄,絕望就會變成希望。","如果不能改變結果,那就完善過程。","好好活就是干有意義的事,有意義的事就是好好活!","你真正是誰並不重要,重要的是你的所做所為。","你不想為你的信仰冒一下險嗎?難道想等你老了,再後悔莫及嗎?","有些鳥兒是關不住的,它的每一根羽毛都閃耀着自由的光輝。","決定我們成為什麼樣人的,不是我們的能力,而是我們的選擇。","掉在水裡你不會淹死,呆在水裡你才會淹死,你只有游,不停的往前游。","有些路,只能一個人走。","希望你眼眸有星辰,心中有山海。","從此以夢為馬,不負韶華。","人的成就和差異決定於其業餘時間。","佛不要你皈依,佛要你歡喜。","ダーリンのこと 大好きだよ","小貓在午睡時,地球在轉。","我,混世大魔王,申請做你的小熊軟糖。","決定好啦,要暗暗努力。","吶,做人呢最緊要開心。","好想邀請你一起去雲朵上打呼嚕呀。","永遠年輕,永遠熱淚盈眶。","我生來平庸,也生來驕傲。","我走得很慢,但我從不後退。","人間不正經生活手冊。","我是可愛的小姑娘,你是可愛。","數學里,有個溫柔霸道的詞,有且僅有。","吧唧一口,吃掉難過。","你頭髮亂了哦。","健康可愛,沒有眼袋。","日月星辰之外,你是第四種難得。","你是否成為了了不起的成年人?","大家都是第一次做人。","何事喧嘩?!","人間有味是清歡。","你笑起來真像好天氣。","風填詞半句,雪斟酒一壺。","除了自渡,他人愛莫能助。","昨日種種,皆成今我。","一夢入混沌 明月撞星辰","保持獨立 適當擁有","謝謝你出現 這一生我很喜歡","做自己就好了 我會喜歡你的","太嚴肅的話,是沒辦法在人間尋歡作樂的","願你餘生可隨遇而安,步步慢。","黃瓜在於拍,人生在於嗨","奇變偶不變,符號看象限。","從來如此,便對么?","今天我這兒的太陽,正好適合曬鈣 你呢","未來可期,萬事勝意。","星光不問趕路人 時光不負有心人","我當然不會試圖摘月,我要月亮奔我而來","女生要修鍊成的五樣東西: 揚在臉上的自信,長在心底的善良, 融進血里的骨氣,刻進命里的堅強,深到骨子里的教養","燕去燕歸,滄海桑田。縱此生不見,平安惟願","我想認識你 趁風不注意","我一直想從你的窗子里看月亮","長大應該是變溫柔,對全世界都溫柔。","別在深夜做任何決定","山中何事,松花釀酒,春水煎茶。","桃李春風一杯酒,江湖夜雨十年燈。","欲買桂花同載酒,終不似,少年游。");
        var le = Math.ceil(Math.random()*a.length); 
        var $i = $("<span></span>").text(a[le]);/*a[a_idx]*/
        /*a_idx = (a_idx + 1) % a.length;
        var x = e.pageX,
        y = e.pageY;
        $i.css({
            "z-index": 999999999999999999999999999999999999999999999999999999999999999999999,
            "top": y - 20,
            "left": x,
            "position": "absolute",
            "font-weight": "bold",
            "color": "rgb("+~~(255*Math.random())+","+~~(255*Math.random())+","+~~(255*Math.random())+")"
        });
        $("body").append($i);
        $i.animate({
            "top": y - 180,
            "opacity": 0
        },
        2000,
        function() {
            $i.remove();
        });
    });
});*/
</script>


<!--音樂,只在PC端寬度>1000px時显示-->
<link rel="stylesheet" href="https://blog-static.cnblogs.com/files/miluluyo/APlayer.min.css">
<div id="player" class="aplayer aplayer-withlist aplayer-fixed" data-id="3116636104" data-server="netease" data-type="playlist" data-order="random" data-fixed="true" data-listfolded="true" data-theme="#2D8CF0"></div>
<script src="https://blog-static.cnblogs.com/files/miluluyo/APlayer.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/Meting.min.js"></script>

<!--貓,只在PC端显示,移動端不加載了,因為會卡頓頁面-->
<script src="https://eqcn.ajz.miesnfu.com/wp-content/plugins/wp-3d-pony/live2dw/lib/L2Dwidget.min.js"></script>
<script>
  var mobile_flag = isMobile();
  if(mobile_flag){
    //console.info("移動端")
  }else{
    //console.info("PC端")
    L2Dwidget.init({
        "model": {
            "jsonPath": "https://unpkg.com/live2d-widget-model-hijiki/assets/hijiki.model.json",
            "scale": 1
        },
        "display": {
            "position": "left",
            "width": 100,
            "height": 200,
            "hOffset": 70,
            "vOffset": 0
        },
        "mobile": {
            "show": true,
            "scale": 0.5
        },
        "react": {
            "opacityDefault": 0.7,
            "opacityOnHover": 0.2
        }
    });
    window.onload = function(){
      $("#live2dcanvas").attr("style","position: fixed; opacity: 0.7; left: 70px; bottom: 0px; z-index: 1; pointer-events: none;")
    }
  }

</script>

<script>

/*記錄訪問數據,我用了兩個,一個是這個在 https://clustrmaps.com/ 網站,另一個是 https://www.51.la/ 這個網站*/
/*
	第一種:https://clustrmaps.com/
	註冊賬號,添加自己博客園的鏈接,選擇自定義Customize,
	選擇 Image based (basic version for websites that don't support javascript),調整到你喜歡的樣式,然後複製
	:這個我插入在了側邊欄的最底部,把生成的代碼粘貼到append內,這就完事了
*/
$('#sideBarMain').append('')

/*
	第二種:https://www.51.la/
	註冊賬號,點控制台,添加統計ID,統計圖標显示我是不显示的
	這個目前插入的js好像報錯,我的是很早之前生成的,還能用,因此還是推薦用第一種吧

	別的地方也有這種很多統計訪問數據的,可以自己找找看呢

*/
</script>

參數說明

名稱 類型 默認值/實例 描述
Youself 字符串 https://www.cnblogs.com/miluluyo/ 個人博客園首鏈接
custom 數組 [{ name:’首頁’, link:’https://www.cnblogs.com/miluluyo/’, istarget:false },{ name:’聯繫’, link:’https://msg.cnblogs.com/send/%E9%BA%8B%E9%B9%BF%E9%B2%81%E5%93%9F’, istarget:true },{ name:’技能樹’, link:’https://miluluyo.github.io/’, istarget:true },{ name:’留言板’, link:’https://www.cnblogs.com/miluluyo/p/11578505.html’, istarget:false },{ name:’相冊’, link:’https://www.cnblogs.com/miluluyo/gallery.html’, istarget:false },{ name:’友鏈’, link:’https://www.cnblogs.com/miluluyo/p/11633791.html’, istarget:false },{ name:’維護’, link:’https://www.cnblogs.com/miluluyo/p/12092009.html’, istarget:false },{ name:’投喂’, link:’https://www.cnblogs.com/miluluyo/p/gratuity.html’, istarget:false },{ name:’管理’, link:’https://i.cnblogs.com/’, istarget:true }] 導航信息
name 導航名
link 導航鏈接
istarget true跳轉到新頁面上,false當前頁面打開
resume 對象 {
“name”:”麋鹿魯喲”,
“link”:”https://www.cnblogs.com/miluluyo/”,
“headurl”:”https://images.cnblogs.com/cnblogs_com/
elkyo/1558759/o_o_my.jpg”,
“introduction”:”大道至簡,知易行難。”
}
自己的友鏈信息
name 導航名
link 導航鏈接
headurl 頭像
introduction 語錄
unionbox 數組 [{
“name”:”麋鹿魯喲”,
“introduction”:”生活是沒有標準答案的。”,
“url”:”https://www.cnblogs.com/miluluyo”,
“headurl”:”https://images.cnblogs.com/cnblogs_com/
elkyo/1558759/o_o_my.jpg”
},{
“name”:”麋鹿魯喲的技能樹”,
“introduction”:”大道至簡,知易行難。”,
“url”:”https://miluluyo.github.io/”,
“headurl”:”https://images.cnblogs.com/cnblogs_com/
elkyo/1558759/o_o_my.jpg”
}]
友鏈數組
name 昵稱
introduction 標語
url 鏈接地址
headurl 頭像地址
clicktext 數組 [{ field: ‘name’, literal: ‘昵稱’, },{ field: ‘introduction’, literal: ‘標語’, },{ field: ‘url’, literal: ‘鏈接地址’, },{ field: ‘headurl’, literal: ‘頭像地址’, }] 友鏈表格頭信息,這項可以忽略掉
logoimg 字符串 ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519070633f12.png’ 瀏覽器頂部小圖標
cuteicon 數組 [‘icon-caomei’,’icon-boluo’,’icon-huolongguo’,’icon-chengzi’,’icon-hamigua’,’icon-lizhi’,’icon-mangguo’,’icon-liulian’,’icon-lizi’,’icon-lanmei’,’icon-longyan’,’icon-shanzhu’,’icon-pingguo’,’icon-mihoutao’,’icon-niuyouguo’,’icon-xigua’,’icon-putao’,’icon-xiangjiao’,’icon-ningmeng’,’icon-yingtao’,’icon-taozi’,’icon-shiliu’,’icon-ximei’,’icon-shizi’] 文章頁面標題前的圖標,此處圖標我只放入了一些水果的icon,不過可以自己引入文件進行修改名字添加自己想加的,本框架有擴展的icon,文件在 github 中的 icon 文件夾內,可以下載去查看
gratuity 字符串 ‘<div class=”popper_box”><p><b>要請我喝奶茶嗎 (づ。◕ᴗᴗ◕。)づ</b> </p><div class=”popper_box_con”><div class=”popper_box_con_li”><img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053817wx.png” alt=””>微信掃碼</div><div class=”popper_box_con_li”><img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053827zfb.png” >支付寶掃碼</div></div><p><b>留下一句你覺得很勵志與美的話給我吧~</b>  <b><a href=”https://www.cnblogs.com/miluluyo/p/12930946.html”>GO</a></b></div>’ 讚賞按鈕焦點显示讚賞內容,內容可自行更改
isGratuity 布爾值 true 默認true,若true則显示此按鈕,false則不显示

更換頂部背景圖

當前框架使用了一張圖片,也可以自己進行更換成隨機圖片API

在css樣式中

 #blogTitle{background:url(https://images.cnblogs.com/cnblogs_com/miluluyo/1764887/o_20051406472117.jpg) center center / cover no-repeat #222;overflow:hidden;width:100%;height:40vh;max-height:40vh;box-shadow:0 1px 2px rgba(150,150,150,.7);       /*搜索這個 更換 background: url() 里的鏈接 即可*/

最後

更多內容請查看 cute-cnblogs 自定義番外篇
(PS:可以使用番外篇里的隨機圖片API喔~)

請吃糖

如果您喜歡這裏,感覺對你有幫助,並且有多餘的軟妹幣的話,不妨投喂一顆糖喔~

<(▰˘◡˘▰)> 謝謝老闆~

微信掃碼

支付寶掃碼

讚賞的時候,留下一句你覺得很勵志與美的話給我吧~

(也可以加一個博客園給我喔,會添加在名字的旁邊喔~點擊可以跳轉~ 例如:去瞧瞧都有誰讚賞了

為了響應大家的號召,方便大家技術交流,之前建立了一個微信群,如果大家有需要可以掃碼(或者搜我微信號:s978761)加我好友,我邀請你加入~本群是一個純交流學習工作的群,不準發布廣告、營銷相關的信息!對了,加我記得備註上:博客園+名稱 加群 喔~

微信號:s978761

公眾號:麋鹿魯喲

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司“嚨底家”!

※推薦評價好的iphone維修中心

聚甘新

布局之: flex(CSS3新增)

flex 基本概念

  flex布局(flex是flexible box的縮寫), 也稱為彈性盒模型 。將屬性和屬性值(display:flex; )寫在哪個標籤樣式中,誰就是 容器;它的所有子元素自動成為容器成員,稱為項目。

當一個元素的display 取值為flex,所有項目(子元素)會在一行显示;如果所有項目的尺寸之和大於容器,也不會超出父元素的寬、高度。不會換行(每個項目都會自動縮小相應的比例)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>布局之:flex</title>
    <link rel="stylesheet" href="./CSS/normalize.css">
    <style>
        section {
            width: 500px;
            height: 800px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
        }
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>
</head>
<body>
    <section>
        <div>01</div>
        <div>02</div>
        <div>03</div>
        <div>04</div>
        <div>05</div>
        <div>06</div>
    </section>
</body>
</html>

頁面效果 : 每一個項目都等比例縮小了。

 

  css代碼分為兩種: 一類是適用於容器的 (設置主軸的起始位置、換行、主軸的對齊方式、多跟軸線對齊方式);一類是適用於項目的(設置項目的位置)。

容器常用的屬性和屬性值

由於重複代碼較多,就不一 一上傳代碼了,大家可以自己動手,敲敲代碼,試試看。

一、設置主軸的起始方向  flex-direction:

默認為X軸(行):

<style>
        section {
            width: 500px;
            height: 500px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            /* flex-direction: row; */
            /* flex-direction: row-reverse; */
            /* flex-direction: column; */
            /* flex-direction: column-reverse; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

 

flex-direction:row; 默認是X軸的起始方向為開始位置 (從左到右依次擺放);
flex-direction:row-reverse; 改變X軸的起始方向為結束位置 (從右到左依次擺放);

設置主軸的起始方向為Y軸(列):

flex-direction:column; 默認是Y軸的起始方向為開始位置(從上到下依次擺放)
flex-direction:column-reverse; 改變Y軸的起始方向為結束位置(從下到上依次擺放)

二、設置項目是否換行  flex-wrap:(默認是不換行)

 <style>
        section {
            width: 400px;
            height: 400px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            /* flex-wrap: wrap; */
            /* flex-wrap: wrap-reverse; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

flex-wrap: nowrap;  默認值是不換行;(n個項目都會在一行显示.如果項目尺寸之和大於容器主軸的尺寸,則項目會自動縮小相應比列.) (參考第一個代碼 頁面結果展示)

flex-wrap: wrap; 設置換行;(超出主軸的寬,則進行換行。換行后,兩行之間會出現間距,是因為垂直方向有剩餘空間,會平均分配給第二行的上下)

flex-wrap: wrap-reverse; 倒序換行;(如果有兩行,第2行显示在前面,第一行显示在後面)

三、主軸方向的對齊方式  justify-content:

項目是一個時:

 <style>
        section {
            width: 400px;
            height: 400px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            /* justify-content: flex-start; */
            /* justify-content: flex-end; */
            /* justify-content: center; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

justify-content:flex-start; 以主軸開始方向對齊 (默認)
justify-content:flex-end; 以主軸結束方向對齊

justify-content:center; 主軸方向居中

項目是多個時:

<style>
        section {
            width: 500px;
            height: 500px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            /* justify-content: space-between; */
            /* justify-content: space-around; */
            /* justify-content: space-evenly; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

justify-content: space-between; 兩端對齊 (第一個項目在容器的起始位置,最後一個項目在容器的結束位置,中間距離相等)

 

justify-content: space-around;  分散對齊

justify-content: space-evenly;  平分剩餘空間,每個項目之間的距離相同

 

四、主軸改變為交叉軸方向的對齊方式

一根軸線:  主軸需改變為Y軸:flex-direction: column;

 

align-items: baseline; 以項目的第一行文字的基線對齊

align-items: stretch; (項目沒有給高的情況下,stretch就是默認值,如果項目沒有設置高度,就是容器的高)

 

 <style>
        section {
width: 500px; height: 500px; border: 2px solid black; margin: 50px auto; display: flex; /* 主軸需改變為Y軸 項目按列擺放 */ flex-direction: column; /* align-items: flex-start; 默認擺放方式 */ /* align-items: center; */ /* align-items: flex-end; */

} div { width: 100px; height: 100px; border: 1px solid tomato; } </style>

 

align-items: flex-start;  交叉軸從開始位置對齊
align-items: center; 交叉軸居中對齊

align-items: flex-end; 交叉軸從結束位置對齊

多根軸線: (所有項目的尺寸之和,必須大於容器的尺寸,使項目換行显示)

<style>
        section {
            width: 500px;
            height: 500px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            flex-direction: column;
            flex-wrap: wrap;
            /* align-content: center; */
            /* align-content: flex-end; */
            /* align-content: space-between; */
            /* align-content: space-around; */
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
    </style>

 

align-content: flex-start; 交叉軸從開始位置對齊
align-content: center; 交叉軸居中對齊

align-content: flex-end; 交叉軸從結束位置對齊

align-content: space-between; 交叉軸兩端對齊

align-content: space-around; 交叉軸分散對齊

align-content: space-evenly; 交叉軸平均分配

 

項目的屬性和屬性值:

一、order 控制項目位置

order:1;
取值 : 正、負數 (默認值是 0)
值越小越靠前 值越大越靠後 。

(適用場景: 1.搜索引擎優化,提升SEO 把重要的信息在html代碼中靠前擺放,但不影響布局 2.調整項目位置)

<style>
        section {
            width: 500px;
            height: 500px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
        
        div:nth-child(4) {
            order: -1;
        }
    </style>

設置一個或多個[項目]在交叉軸的對齊方式:

 <style>
        section {
            width: 800px;
            height: 400px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
        
        div:nth-child(2) {
            align-self: center;
        }
        
        div:nth-child(3) {
            align-self: flex-end;
        }
    </style>

align-self: flex-start; 設置項目在交叉軸開始位置擺放 (默認位置)
align-self: center; 設置項目在交叉軸居中擺放

align-self: flex-end; 設置項目在交叉軸結束位置擺放

設置某一個或多個元素放大比例

  條件:所有項目的尺寸之和要小於容器的尺寸
  (沒有剩餘空間,則設置此屬性無效。)

一個元素有 flex-grow 屬性

<style>
        section {
            width: 800px;
            height: 400px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
        }
        
        div:nth-child(2) {
            flex-grow: 1;
        }
    </style>

多個項目有flex-grow 屬性

<style>
        section {
            width: 800px;
            height: 200px;
            border: 2px solid black;
            margin: 50px auto;
            display: flex;
            box-sizing: border-box;
        }
        
        div {
            width: 100px;
            height: 100px;
            border: 1px solid tomato;
            box-sizing: border-box;
        }
        
        div:nth-child(2) {
            flex-grow: 1;
        }
        
        div:nth-child(4) {
            flex-grow: 2;
        }
    </style>

效果展示

將容器的剩餘空間分成相應的flex-grow的份數,再按照每個項目的份數,分給有flex-grow屬性的項目。

 

  總之,flex使用起來特別方便,可適用於響應式布局,也可使用聖杯布局。只是屬性較多,也要多練、多實踐 ,相信你也能很快熟練使用flex的。

推薦一個小遊戲,很有趣,又能增強關於flex的使用方法 :Flexbox Froggy  http://blog.xiaoboswift.com/flexbox/#zh-cn  去幫助小青蛙回家吧~~

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

聚甘新

.Net Core服務監控報警指標上報Prometheus+Grafana

前言

簡單集成Prometheus+Grafana,指標的上報收集可視化。

Prometheus

Prometheus是一個監控平台,監控從HTTP端口收集受監控目標的指標。在微服務的架構里Prometheus多維度的數據收集是非常強大的 我們首先下載安裝Prometheusnode_exporter,node_exporter用於監控CPU、內存、磁盤、I/O等信息

  • Prometheus下載地址
  • node_exporter下載地址

下載完成后解壓以管理員運行 prometheus.exe 訪問 http://localhost:9090/ 出現一下頁面說明啟動成功啦

.Net Core獲取指標

有了Prometheus,我們還需要給Prometheus提供獲取監控數據的接口,我們新建一個WebApi項目,並導入prometheus-net.AspNetCore包,在Configure中加入UseMetricServer中間件

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

    app.UseMetricServer();
    
}

啟動項目訪問http://localhost:5000/metrics就可以看基本的一些監控信息啦,包括線程數,句柄數,3個GC的回收計數等信息。

# HELP process_num_threads Total number of threads
# TYPE process_num_threads gauge
process_num_threads 29
# HELP process_working_set_bytes Process working set
# TYPE process_working_set_bytes gauge
process_working_set_bytes 44441600
# HELP process_private_memory_bytes Process private memory size
# TYPE process_private_memory_bytes gauge
process_private_memory_bytes 69660672
# HELP dotnet_total_memory_bytes Total known allocated memory
# TYPE dotnet_total_memory_bytes gauge
dotnet_total_memory_bytes 2464584
# HELP dotnet_collection_count_total GC collection count
# TYPE dotnet_collection_count_total counter
dotnet_collection_count_total{generation="1"} 0
dotnet_collection_count_total{generation="0"} 0
dotnet_collection_count_total{generation="2"} 0
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1592448124.2853072
# HELP process_open_handles Number of open handles
# TYPE process_open_handles gauge
process_open_handles 413
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 2225187631104
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 1.171875

Help 是收集指標的說明,Type收集指標的類型

但是作為HTTP應用怎麼能沒有HTTP的監控和計數呢,只需要加加入UseHttpMetrics中間件就可以對HTTP請求監控和計數,主要注意的是UseHttpMetrics最好放在UseEndpointsUseRouting中間

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseMetricServer();
    
    app.UseRouting();
    
    app.UseHttpMetrics();

    app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}

啟動項目繼續訪問http://localhost:5000/metrics

# HELP http_requests_in_progress The number of requests currently in progress in the ASP.NET Core pipeline. One series without controller/action label values counts all in-progress requests, with separate series existing for each controller-action pair.
# TYPE http_requests_in_progress gauge

可以看到已經有了,我們隨便請求一下服務看看效果,會幫我們記錄下總耗時,總請求數,和每次請求的耗時數

但是單單有上面那些數據好像還不太好定位一下很奇葩的問題,這時候我們可以獲取Runtime的一些數據,方法童謠很簡單。導入prometheus-net.DotNetRuntime 包,它可以幫助我們看到如下指標

  • 垃圾回收的收集頻率和時間
  • 服務佔用堆大小
  • 對象堆分配的字節
  • JIT編譯和JIT CPU消耗率
  • 線程池大小,調度延遲以及增長/縮小的原因
  • 鎖爭用情況

我們只需要在ProgramMain方法中啟動收集器就可以啦。

public static void Main(string[] args)
{
    DotNetRuntimeStatsBuilder.Default().StartCollecting();
    CreateHostBuilder(args).Build().Run();
}

啟動項目繼續訪問http://localhost:5000/metrics測試一下

# HELP dotnet_collection_count_total GC collection count
# TYPE dotnet_collection_count_total counter
dotnet_collection_count_total{generation="1"} 0
dotnet_collection_count_total{generation="0"} 0
dotnet_collection_count_total{generation="2"} 0
# HELP process_private_memory_bytes Process private memory size
# TYPE process_private_memory_bytes gauge
process_private_memory_bytes 75141120
# HELP dotnet_gc_pause_ratio The percentage of time the process spent paused for garbage collection
# TYPE dotnet_gc_pause_ratio gauge
dotnet_gc_pause_ratio 0
# HELP http_requests_received_total Provides the count of HTTP requests that have been processed by the ASP.NET Core pipeline.
# TYPE http_requests_received_total counter
# HELP dotnet_gc_collection_seconds The amount of time spent running garbage collections
# TYPE dotnet_gc_collection_seconds histogram
dotnet_gc_collection_seconds_sum 0
dotnet_gc_collection_seconds_count 0
dotnet_gc_collection_seconds_bucket{le="0.001"} 0
dotnet_gc_collection_seconds_bucket{le="0.01"} 0
dotnet_gc_collection_seconds_bucket{le="0.05"} 0
dotnet_gc_collection_seconds_bucket{le="0.1"} 0
dotnet_gc_collection_seconds_bucket{le="0.5"} 0
dotnet_gc_collection_seconds_bucket{le="1"} 0
dotnet_gc_collection_seconds_bucket{le="10"} 0
dotnet_gc_collection_seconds_bucket{le="+Inf"} 0
# HELP dotnet_total_memory_bytes Total known allocated memory
# TYPE dotnet_total_memory_bytes gauge
dotnet_total_memory_bytes 4925936
# HELP dotnet_threadpool_num_threads The number of active threads in the thread pool
# TYPE dotnet_threadpool_num_threads gauge
dotnet_threadpool_num_threads 0
# HELP dotnet_threadpool_scheduling_delay_seconds A breakdown of the latency experienced between an item being scheduled for execution on the thread pool and it starting execution.
# TYPE dotnet_threadpool_scheduling_delay_seconds histogram
dotnet_threadpool_scheduling_delay_seconds_sum 0.015556
dotnet_threadpool_scheduling_delay_seconds_count 10
dotnet_threadpool_scheduling_delay_seconds_bucket{le="0.001"} 0
dotnet_threadpool_scheduling_delay_seconds_bucket{le="0.01"} 10
dotnet_threadpool_scheduling_delay_seconds_bucket{le="0.05"} 10
dotnet_threadpool_scheduling_delay_seconds_bucket{le="0.1"} 10
dotnet_threadpool_scheduling_delay_seconds_bucket{le="0.5"} 10
dotnet_threadpool_scheduling_delay_seconds_bucket{le="1"} 10
dotnet_threadpool_scheduling_delay_seconds_bucket{le="10"} 10
dotnet_threadpool_scheduling_delay_seconds_bucket{le="+Inf"} 10
# HELP process_working_set_bytes Process working set
# TYPE process_working_set_bytes gauge
process_working_set_bytes 50892800
# HELP process_num_threads Total number of threads
# TYPE process_num_threads gauge
process_num_threads 32
# HELP dotnet_jit_method_seconds_total Total number of seconds spent in the JIT compiler
# TYPE dotnet_jit_method_seconds_total counter
dotnet_jit_method_seconds_total 0
dotnet_jit_method_seconds_total{dynamic="false"} 0.44558800000000004
dotnet_jit_method_seconds_total{dynamic="true"} 0.004122000000000001
# HELP dotnet_gc_pinned_objects The number of pinned objects
# TYPE dotnet_gc_pinned_objects gauge
dotnet_gc_pinned_objects 0
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1592449942.6063592
# HELP dotnet_gc_heap_size_bytes The current size of all heaps (only updated after a garbage collection)
# TYPE dotnet_gc_heap_size_bytes gauge
# HELP http_request_duration_seconds The duration of HTTP requests processed by an ASP.NET Core application.
# TYPE http_request_duration_seconds histogram
# HELP dotnet_contention_seconds_total The total amount of time spent contending locks
# TYPE dotnet_contention_seconds_total counter
dotnet_contention_seconds_total 0
# HELP dotnet_gc_pause_seconds The amount of time execution was paused for garbage collection
# TYPE dotnet_gc_pause_seconds histogram
dotnet_gc_pause_seconds_sum 0
dotnet_gc_pause_seconds_count 0
dotnet_gc_pause_seconds_bucket{le="0.001"} 0
dotnet_gc_pause_seconds_bucket{le="0.01"} 0
dotnet_gc_pause_seconds_bucket{le="0.05"} 0
dotnet_gc_pause_seconds_bucket{le="0.1"} 0
dotnet_gc_pause_seconds_bucket{le="0.5"} 0
dotnet_gc_pause_seconds_bucket{le="1"} 0
dotnet_gc_pause_seconds_bucket{le="10"} 0
dotnet_gc_pause_seconds_bucket{le="+Inf"} 0
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 2225201872896
# HELP dotnet_gc_finalization_queue_length The number of objects waiting to be finalized
# TYPE dotnet_gc_finalization_queue_length gauge
dotnet_gc_finalization_queue_length 0
# HELP dotnet_threadpool_io_num_threads The number of active threads in the IO thread pool
# TYPE dotnet_threadpool_io_num_threads gauge
dotnet_threadpool_io_num_threads 3
# HELP process_open_handles Number of open handles
# TYPE process_open_handles gauge
process_open_handles 436
# HELP dotnet_gc_collection_reasons_total A tally of all the reasons that lead to garbage collections being run
# TYPE dotnet_gc_collection_reasons_total counter
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 0.890625
# HELP http_requests_in_progress The number of requests currently in progress in the ASP.NET Core pipeline. One series without controller/action label values counts all in-progress requests, with separate series existing for each controller-action pair.
# TYPE http_requests_in_progress gauge
# HELP dotnet_threadpool_adjustments_total The total number of changes made to the size of the thread pool, labeled by the reason for change
# TYPE dotnet_threadpool_adjustments_total counter
# HELP dotnet_jit_cpu_ratio The amount of total CPU time consumed spent JIT'ing
# TYPE dotnet_jit_cpu_ratio gauge
dotnet_jit_cpu_ratio 0.5728901224489797
# HELP process_cpu_count The number of processor cores available to this process.
# TYPE process_cpu_count gauge
process_cpu_count 8
# HELP dotnet_build_info Build information about prometheus-net.DotNetRuntime and the environment
# TYPE dotnet_build_info gauge
dotnet_build_info{version="3.3.1.0",target_framework=".NETCoreApp,Version=v5.0",runtime_version=".NET Core 5.0.0-preview.2.20160.6",os_version="Microsoft Windows 10.0.18363",process_architecture="X64"} 1
# HELP dotnet_jit_method_total Total number of methods compiled by the JIT compiler
# TYPE dotnet_jit_method_total counter
dotnet_jit_method_total{dynamic="false"} 830
dotnet_jit_method_total{dynamic="true"} 30
# HELP dotnet_gc_cpu_ratio The percentage of process CPU time spent running garbage collections
# TYPE dotnet_gc_cpu_ratio gauge
dotnet_gc_cpu_ratio 0
# HELP dotnet_threadpool_scheduled_total The total number of items the thread pool has been instructed to execute
# TYPE dotnet_threadpool_scheduled_total counter
dotnet_threadpool_scheduled_total 16
# HELP dotnet_gc_allocated_bytes_total The total number of bytes allocated on the small and large object heaps (updated every 100KB of allocations)
# TYPE dotnet_gc_allocated_bytes_total counter
dotnet_gc_allocated_bytes_total{gc_heap="soh"} 3008088
dotnet_gc_allocated_bytes_total{gc_heap="loh"} 805392
# HELP dotnet_contention_total The number of locks contended
# TYPE dotnet_contention_total counter
dotnet_contention_total 0

可以看到非常多的信息啦,但是我們有時候不需要這麼多指標也可以自定義。


public static void Main(string[] args)
{
    DotNetRuntimeStatsBuilder
        .Customize()
        .WithContentionStats()
        .WithJitStats()
        .WithThreadPoolSchedulingStats()
        .WithThreadPoolStats()
        .WithGcStats()
        .StartCollecting();
    CreateHostBuilder(args).Build().Run();
}

JIT,GC和線程的監控是會影響到一點點性能,我們可以通過sampleRate這個枚舉的值來控制採樣頻率

public static void Main(string[] args)
{
    DotNetRuntimeStatsBuilder
        .Customize()
        //每5個事件個採集一個
        .WithContentionStats(sampleRate: SampleEvery.FiveEvents)
        //每10事件採集一個
        .WithJitStats(sampleRate: SampleEvery.TenEvents)
        //每100事件採集一個
        .WithThreadPoolSchedulingStats(sampleRate: SampleEvery.HundredEvents)
        .WithThreadPoolStats()
        .WithGcStats()
        .StartCollecting();
    CreateHostBuilder(args).Build().Run();
}

有了這些指標我們需要Prometheus來收集我們Api的指標,只需要修改prometheus.yml文件然後重啟Prometheus就可以了。

scrape_configs:  
- job_name: mydemo  
  scrape_interval: 15s  
  scrape_timeout: 10s  
  metrics_path: /metrics  
  scheme: http  
  static_configs:  
  - targets:  
    - localhost:5000   

啟動Api項目和Prometheus,選中dotnet_collection_count_total點擊Excute可以看到Api的指標是正常上報的。

Prometheus有了數據我們就需要一個炫酷的UI去展示上報的數據啦。

Grafana

Prometheus有了數據就差一個漂亮的UI來展示的我們的指標了。Grafana是一個Go編寫的開源應用,用於把指標數據可視化。是當下流行的時序數據展示工具。先下載,直接下載exe安裝,完成后能打開http://localhost:3000/頁面就安裝成功了

  • 下載地址

先添加數據源,選擇Prometheus為數據源,並配置。

添加儀錶盤

Import via panel json中加入下面這個json,點擊load,

  • 儀錶盤json

選擇數據源,點擊Import就能看到儀錶盤了

還可以去這裏添加很多現有的儀錶盤。複製ID添加儀錶盤。

參考文章

prometheus-net
.NetCore下使用Prometheus實現系統監控和警報系列

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

聚甘新

vs.net/vscode中使用Beetlex創建vue應用

      平時在開發Vue應用則需要安裝nodejs,vue cli等相關東西相對來說麻煩一些;如果你喜歡像vs.net/vscode創建普通項目一樣就能開發Vue項目的話那可以嘗試一下BeetleX針對Vue編寫的服務插件;只需要創建一個簡單的Console項目引用相關插件即可以構建一個單頁面的Vue項目。雖然在開發的時候需要用到Beetlex,但後期發布完全可以用在其他平台上,因為組件會針對Vue的內容最終生成一個可發布的js文件。接下來介紹一下這個插件的使用(vs.net/vscode均可)

創建項目

首先需要創建一個c#的Console項目

創建項目后需要引用兩個BeetleX的組件包,可通過Nuget安裝最新版本;分別是:BeetleX.FastHttpApi.HostingBeetleX.FastHttpApi.VueExtend;這兩個組件的作用分別是在項目中啟動HTTP服務和針對.VUE文件生成相應的javascript文件。

項目文件布局

由於是Console項目,所以需要針對相關文件存放規則,具體大概如下:

所有html,css,js和vue等文件必須存放在項目的views目錄下;對於這個目錄下用什麼子目錄存放相關文件就看自己的需求了。

服務和基礎資源配置

為了讓控制台服務作為一個HTTP服務需要做一些簡單的配置

static void Main(string[] args)
{
    var builder = new HostBuilder()
        .ConfigureServices((hostContext, services) =>
        {
            services.UseBeetlexHttp(o =>
            {
                o.Port = 80;
                o.SetDebug();
                o.LogToConsole = true;
                o.LogLevel = BeetleX.EventArgs.LogType.Info;
            },
            s =>
            {
                s.AddExts("woff;ttf;woff2");
                s.Vue().CssRewrite("/css/{1}.css").JsRewrite("/js/{1}.js");
                s.Vue().Debug();
                var resource = s.Vue().CreateWebResorce(typeof(Program).Assembly);
                resource.AddScript("vue.js", "axios.js", "beetlex4axios.js", "jquery.js", "echarts.js", "bootstrap.js", "page.js");
                resource.AddCss("bootstrap.css", "bootstrapadmin.css", "admin.css");
            },
            typeof(Program).Assembly);
        });
    builder.Build().Run();
}

前部分主要描述在那個端口開發HTTP服務,並設置相關日誌显示級別;後半部分主要是描述vue配置一些信息。這個後面會詳細描述,接下來看啟動一下服務看下日誌。

服務日誌會显示資源加載和服務端口的情況。

VUE擴展配置

前面服務啟動的時候就已經配置相關VUE的內容,這裏再詳細解說一下。

s.Vue().Debug();
s.Vue().CssRewrite("/css/{1}.css").JsRewrite("/js/{1}.js");
var resource = s.Vue().CreateWebResorce(typeof(Program).Assembly);
resource.AddScript("vue.js", "axios.js", "beetlex4axios.js", "jquery.js", "echarts.js", "bootstrap.js","page.js");
resource.AddCss("bootstrap.css", "bootstrapadmin.css", "admin.css");

Beetlex的Vue插件會管理項目的兩種資源,分別是css和javascript. 

  • Debug方法

        主要是告訴組件每次調用資源都重新從文件中生成,這樣開發都在變更相關文件的時候無須重新編譯,保存文件后刷頁面即可。此方法在Release編譯模式下並不生效。

  • CssRewrite和JsRewrite方法

         這兩個方法主要是描述通過那些路徑訪問到css和javascript資源,以上定義/css/路徑任意一文件都會得到項目中所有的css內容;/js/路徑任意文件都得到項目的javascript內容。

  • WebResource

        這個類用於描述如何收集對應的css和javascript文件;對於javascript文件來說會先打包這些基礎的文件然後再追加項目中的vue文件。打包的順序是依據定義的順序來進行。

啟動頁

在項目vue文件只是模塊文件,我們需要在根目錄下定義一個HTML文件作為訪問落地頁面,接下來看一下這頁面的定義

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <link href="/css/v1.css?group=BeetleX.AdminUI" rel="stylesheet" />
    <script src="/js/v1.js?group=BeetleX.AdminUI"></script>
    <title>BeetleX AdminUI</title>
</head>
<body>
    <div id="page">
        <main_menu @menu_resize="OnMenuResize($event)" @openwindow="OnOpenWindow($event)"></main_menu>
        <windows_bar :windows="windows" :full="full" :selectwindow="selectWindow.id" @close="OnCloseWindows($event)"></windows_bar>
        <div class="main-content" :style="{left:(full=='max'?'60px':'260px')}">
            <keep-alive>
                <component :is="selectModel" @openwindow="OnOpenWindow($event)" :token="selectWindow.data" :winsize="sizeVersion"></component>
            </keep-alive>
        </div>
        <page_footer></page_footer>
    </div>
    <script>
        var page = new Vue(pageInfo);
        page.OnOpenWindow({ id: 'home', title: '主頁', model: 'models_home' })
    </script>
</body>
</html>

頁面只是負責資源加載和VUE模塊組裝,在這裏定義的css和javascript加載組是Beetlex.AdminUI即對應剛才加載的程序集資源包:

var resource = s.Vue().CreateWebResorce(typeof(Program).Assembly);

這個落地頁的展示效果如下:

模塊定義

項目配置完成后就可以在views目錄定義自己的vue模塊,存放層次目錄沒有具體的要求可根據自己的喜好來定義存放目錄.對於vue模塊的定義和傳統的vue定義會有些差別的,模塊文件名作為對應的模塊名稱。文件內部主要有HTML和JavaScript組成,而不是像傳統那樣一個vue文件和一個js文件。下面是一個models_home.vue模塊的描述:

    <div style="width:99%;">
        <div class="row">
            <div class="col-lg-6">
                <models_panel :title="'銷售走勢'" :child="'models_monthline'" :winsize="winsize"></models_panel>
                <models_panel :title="'僱員銷售比例'" :child="`model_employeesspie`" :winsize="winsize"></models_panel>
            </div>
            <div class="col-lg-6">
                <models_panel :title="'最新訂單'" :child="`models_neworders`"></models_panel>
                <models_panel :title="'客戶訂單比例'" :child="`model_customerspie`" :winsize="winsize"></models_panel>
            </div>
        </div>
    </div>
<script>
 {
        props: ["winsize"],
        data: function () {
            return {
               
            }
        },
    }
</script>

組件支持的VUE模塊描述要相對簡單一些,沒有一些import的東西;只有HTML和一個VUE構造信息的結構體。

接下來是一個簡單的列表模塊models_employees.vue:

    <div>
        <table class="table">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Title</th>
                    <th>Region</th>
                    <th>City</th>
                    <th>Country</th>
                    <th>Address</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="item in GetEmployees.result">
                    <td><a href="javascript:void(0)" @click="OnOpen(item)">{{item.FirstName}} {{item.LastName}}</a> </td>
                    <td>{{item.Title}}</td>
                    <td>{{item.Region}}</td>
                    <td>{{item.City}}</td>
                    <td>{{item.Country}}</td>
                    <td>{{item.Address}}</td>
                </tr>
            </tbody>
        </table>
    </div>
<script>
    {
        data: function () {
            return {
                GetEmployees: new beetlexAction("/Employees", null, []),
            }
        },
        methods: {
            OnOpen: function (item) {
                this.$open('emp' + item.EmployeeID, '僱員:' + item.FirstName + ' ' + item.LastName, 'models_employeedetail', { id: item.EmployeeID });
            }
        },
        mounted: function () {
            this.GetEmployees.get();
        }
    }
</script>

發布

Beetlex原本是一個HTTP服務模塊,正常情況你把相關文件嵌入到項目發布即可在Linux或windows下運行(環境.net core 2.1或更高版本)。如果你不相基於Beetlex運行,那你可以在編譯目錄下獲取相關的javascript和css完全打包好的文件放到其他環境中部署。

代碼

如果你感興趣可以訪問 https://github.com/IKende/AdminUI   演示地址: http://adminui.beetlex.io/

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

聚甘新

超詳細Maven技術應用指南

該文章,GitHub已收錄,歡迎老闆們前來Star!

GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual

搜索關注微信公眾號“碼出Offer”,送你學習福利資源!

一、前言

在我們的項目資源中,你會發現需要導入的jar包越來越多,讓jar包的管理越來越沉重。它會表現為以下幾個缺點:

  • 每個項目都需要手動搜集和導入所需要的jar包
  • 項目中用到的jar包有版本更新,我們需要重新搜集並導入到項目中
  • 相同的jar包導入到不同的項目中,jar包會在本地存儲多份

針對上述問題,我們就需要使用統一的管理工具:Maven

二、了解Maven

2.1 什麼是Maven

Maven是一個基於項目對象模型(POM)的概念的純Java開發的開源的項目管理工具。主要用來管理Java項目,進行依賴管理(jar包依賴管理)和項目構建(項目編譯、打包、測試、部署)。此外還能分模塊開發,提高開發效率。

2.2 Maven的下載安裝

關於Maven的下載,我們需要下載它的解壓包。

Maven下載地址: https://us.mirrors.quenda.co/apache/maven/maven-3/3.6.3/binaries/

image-20200616171323409

下載后將Maven解壓到目錄中就可以了!

注意: 解壓的目錄與tomact服務器的形式是一樣的,不要有中文及特殊符號!

image-20200616171637526

2.3 Maven目錄結構解析

目錄名稱 描述
bin 存儲mvn的各種可執行文件
boot 含有plexus-classworlds類加載器框架,Maven 使用該框架加載自己的類庫
conf 存放settings.xml等配置文件
lib 存儲Maven運行時所需要的Java類庫
LICENSE/NOTICE/README.txt 針對Maven的版本、第三方軟件等簡要介紹

2.4 配置環境變量

Maven依賴Java環境的配置環境,所以要確保jdk版本在1.7以上,maven版本在3.3以上。

  • 配置環境變量與jdk環境變量配置是一樣的,在本機中創建MAVEN_HOME環境變量,並將maven的解壓路徑設置進去,點擊確定(路徑參考上圖解壓后的結果圖路徑)
  • 修改path環境變量,添加%MAVEN_HOME%\bin后,一路點擊確定即可!

2.5 測試

下載解壓、配置環境變量后,我們打開DOS命令窗口,鍵入mvn -v查看maven版本信息

  • 如果看到如下圖片maven的版本信息,證明maven安裝配置成功!
  • 在Maven的版本信息你就可以得知它依賴於jdk環境!

image-20200616172931556

2.6 Maven項目模型圖

三、Maven的配置

3.1 配置本地倉庫

本地倉庫簡單來說,就是在本地的maven中存儲管理所需jar包

  1. 首先,打開maven目錄conf文件夾中的settings.xml配置文件

  2. 其次,找到標號1的那一行配置信息,並複製此配置信息放在其下面

  3. 然後,在磁盤中創建一個目錄,作為存儲jar文件的本地倉庫

  4. 最後,將複製的此配置信息路徑替換成自己創建的本地倉庫目錄路徑,參考標號2的操作

image-20200616174928266

3.2 配置jdk

3.2.1 全局配置

由於Maven依賴於jdk環境,所以我們也需要在maven中配置jdk(我使用的jdk是主流的1.8版本)

  1. 打開settings.xml配置文件,找到<profiles>標籤,你會發現標籤內都是註釋的內容,我們需要在標籤內,寫入自己的jdk的配置信息。配置如下:
  2. 在maven中添加好jdk的配置信息后,我們需要在</profiles>結束標籤后添加<activeProfiles>標籤內容,讓配置好的<profiles>標籤中內容生效

注意: profile標籤中的id是此配置信息的名稱,在後面使用activeProfile標籤讓其配置生效的時候,需要保證id與activeProfile的名稱一致!(貼圖供大家參考!)

    <!-- 配置jdk -->
    <profile>
        <id>jdk1.8</id>
        <activation>
            <activeByDefault>true</activeByDefault>    
            <jdk>1.8</jdk>    
        </activation>    
        <properties>    
            <maven.compiler.source>1.8</maven.compiler.source>    
            <maven.compiler.target>1.8</maven.compiler.target>
            <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> 
        </properties>    
    </profile>
<!-- 使配置好的profiles標籤中內容生效 -->
<activeProfiles>
    <activeProfile>jdk1.8</activeProfile>
</activeProfiles>

image-20200616181057029

3.2.2 單個項目修改

後面我們會了解到maven項目是通過pom.xml進行構建信息配置和依賴信息配置。其中就包括配置編譯需要的jdk版本。所以我們直接修改pom文件就可以實現單個項目修改,但是我們並不推薦此種方式,因為這個方式需要每個項目都要修改,不具有可重用性!

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
</build>

四、倉庫

4.1 倉庫概念

  • 存儲依賴的地方,體現形式就是本地的一個目錄。
  • 倉庫中不僅存放依賴,而且管理着每個依賴的唯一標識(坐標),Java項目憑坐標獲取依賴。

4.2 倉庫分類

Maven倉庫可以分為本地倉庫和遠程倉庫,其中遠程倉庫又分為中央倉庫、公共倉庫和私服

  • 本地倉庫: 本地倉庫存放着項目中所需jar文件
  • 中央倉庫: Maven的中央倉庫是由Maven社區提供存儲jar文件的倉庫
  • 公共倉庫: 國內廠家提供的存儲jar文件的倉庫,比如:aliyun倉庫
  • 私服: 由公司創建的存儲jar文件的倉庫,可在公司範圍內共享,不對外開放

當項目中需要jar文件依賴時,會從倉庫中查找獲取,如果我們把所有倉庫都配置好。maven在查找獲取依賴的時候遵循一個依賴查找順序,如下:(如果本地倉庫找不到依賴就去私服下載,以此類推……)

依賴查找順序: 本地倉庫 – > 私服 – > 公共倉庫 – > 中央倉庫

image-20200616184606968

4.3 本地倉庫

本地倉庫: 本地目錄中存放所需jar包,需修改settings配置文件來配置本地倉庫

  • 使用過的依賴都會存儲在本地倉庫中,實現復用

4.4 遠程倉庫

4.4.1 中央倉庫

中央倉庫: Maven中央倉庫是由Maven社區提供的倉庫,不用任何配置,maven中內置了中央倉庫地址。其中就包含絕大多數流行的開源Java構件

  • https://mvnrepository.com/可以搜索需要的依賴的相關信息(倉庫搜索服務)
  • http://repo.maven.apache.org/maven2/為中央倉庫地址
4.4.2 公共倉庫

公共倉庫: 第三方維護的jar文件倉庫,比如阿里雲提供的倉庫。但是jar文件可能不如官方的中央倉庫全,有時候也會找不到,所以如果項目構建不成功,可以更改鏡像為官方的,下載完jar包再去改回來

  • aliyun倉庫地址:http://maven.aliyun.com/nexus/content/groups/public/

  • 因為Maven社區提供的中央倉庫在國外,國內使用下載依賴速度過慢,所以一般我們都配置國內的公共倉庫來代替中央倉庫

  • 使用時,需要在settings.xml配置文件中添加配置信息。打開settings.xml配置文件,找到<mirrors>標籤,你也會發現這是一個空標籤,最後標籤內填寫如下配置就OK!

    <!--setting.xml中添加如下配置-->
    <mirror>
        <id>aliyun</id>  
        <!-- 中心倉庫的 mirror(鏡像) -->
        <mirrorOf>central</mirrorOf>    
        <name>Nexus aliyun</name>
        <!-- aliyun倉庫地址 以後所有要指向中心倉庫的請求,都會指向aliyun倉庫-->
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>  
    </mirror>

image-20200616223651045

4.4.3 私服

私服: 公司創建存儲jar文件的倉庫,只對公司範圍共享,不對外開放。可以通過Nexus來創建、管理一個私服。

4.4.3.1 私服概念
  • 私服是架設在局域網的一種特殊的遠程倉庫,目的是代理遠程倉庫及部署第三方構件。

  • 有了私服之後,當 Maven 需要下載依賴時,直接請求私服,私服上存在則下載到本地倉庫;否則,私服請求外部的遠程倉庫,將構件下載到私服,再提供給本地倉庫下載。

  • 私服可以解決在企業做開發時每次需要的jar包都要在中心倉庫下載,且每次下載完只能被自己使用,不能被其他開發人員使用

  • 所謂私服就是一個服務器,但是不是本地層面的,是公司層面的,公司中所有的開發人員都在使用同一個私服

4.4.3.2 私服架構

我們可以使用專門的 Maven 倉庫管理軟件來搭建私服,比如:Apache Archiva,Artifactory,Sonatype Nexus。這裏我們使用Sonatype Nexus

  • 我們可以在圖中得到在無私服的情況下,我們都需要從遠程倉庫去獲取jar文件並存儲在本地倉庫,由於中央倉庫是國外的,所以下載速度比較慢等等原因,並存在很多缺點,比如我們公司內使用
  • 我們在圖中可清晰的看到,如果有私服的話,我們需要從私服中,查找jar文件,如果私服沒有該jar文件,就需要去中央倉庫或公共倉庫去下載,然後傳到私服中,最後傳入到自己的本地倉庫進行使用。假設公司員工再一次使用到該jar文件,它會先從私服中找有沒有這個jar文件,由於我們之前的員工已經將該jar文件存儲到了私服中,所以就省去了其他員工調用遠程倉庫的步驟。並且私服是公司內部局域網類型的,下載速度會比遠程倉庫快出很多倍
無私服 有私服
私服1 私服2
4.4.3.3 Nexus安裝(了解即可)
  • Nexus官網地址:https://blog.sonatype.com/

  • 下載地址:https://help.sonatype.com/repomanager2/download/download-archives—repository-manager-oss

下載我們需要下載Zip解壓包即可,將解壓包解壓到本地盤符中即可!

解壓后,你會看到nexus目錄為私服目錄,sonatype-work目錄中包含存儲私服下載的依賴。

4.4.3.4 Nexus登錄

訪問私服:http://localhost:8081/nexus/

登錄私服的賬號為admin,密碼為admin123

4.4.3.5 倉庫列表
倉庫類型 描述
group 包含多個倉庫,通過group庫的地址可以從包含的多個倉庫中查找構件
hosted 私服 服務器本地的倉庫,其中存儲諸多構件
proxy 代理倉庫,其會關聯一個遠程倉庫, 比如中央倉庫,aliyun倉庫,向該倉庫查找構件時,如果沒有會從其關聯的倉庫中下載
倉庫名 描述
Releases 存放項目的穩定發布版本,一個模塊做完后如果需要共享給他人,可以上傳到私服的該庫
Snapshots 對應不穩定的發布版本
3rd party 存放中央倉庫沒有的 ,如ojdbc.jar,可以上傳到私服的該庫中
倉庫列表
私服_list
4.4.3.6 倉庫組

而此時就有問題,私服中有很多倉庫,每個倉庫都有自己的url,則項目中配置哪個倉庫呢 ?

私服中有一個倉庫組,組中包含多個倉庫,可以指定倉庫組的url,即可從多個倉庫中獲取構件

關於倉庫的設置: 由於我們在使用私服的時候,本地倉庫沒有的jar文件,需要去私服找,私服沒有的話,就去中央倉庫找。所以我們需要把私服內的中央倉庫換為阿里雲倉庫,這樣可以保證我們國內的下載速度。

倉庫組 注意:proxy的倉庫排序在最後
私服_deploy2
4.4.3.7 手動上傳倉庫
4.4.3.8 Maven關聯私服

配置settings.xml,設置私服地址、認證等信息(關聯私服需要添加配置文件信息如下,找到父標籤,添加子標籤內容即可)

<servers>
    <server> 
        <id>nexus-public</id> <!-- nexus的認證id -->
        <username>admin</username> <!--nexus中的用戶名密碼-->
        <password>admin123</password> 
    </server>
</servers>
<profiles>
    <profile> 
        <id>nexus</id> 
        <repositories> 
            <repository> 
                <id>nexus-public</id> <!--nexus認證id 【此處的repository的id要和 <server>的id保持一致】-->
                <!--name隨便-->
                <name>Nexus Release Snapshot Repository</name> 
                <!--地址是nexus中倉庫組對應的地址-->
                <url>http://localhost:8081/nexus/content/groups/public/</url>
                <releases><enabled>true</enabled></releases> 
                <snapshots><enabled>true</enabled></snapshots> 
            </repository>
        </repositories> 
        <pluginRepositories> <!--插件倉庫地址,各節點的含義和上面是一樣的-->
            <pluginRepository> 
                <id>nexus-public</id> <!--nexus認證id 【此處的repository的id要和 <server>的id保持一致】-->
                <!--地址是nexus中倉庫組對應的地址-->
                <url>http://localhost:8081/nexus/content/groups/public/</url>
                <releases><enabled>true</enabled></releases> 
                <snapshots><enabled>true</enabled></snapshots> 
            </pluginRepository> 
        </pluginRepositories> 
    </profile>
</profiles>
<activeProfiles>
    <activeProfile>yourjdk</activeProfile>
    <!-- 使私服配置生效 -->
    <activeProfile>nexus</activeProfile>
</activeProfiles>
4.4.3.9 Meven項目部署到私服
  • 執行mvn deploy指令即可將項目部署到私服對應的倉庫中,此時項目中的打包方式多為jar
  • 但需要提前在項目的pom.xml中配置部署私服倉庫位置,如下:

注意: 如上的 repository的 id 依然是要和settings.xml中配置的server中的id 一致,才能通過私服的認證

<project>
        ...
    <dependencies>
        .....
    </dependencies>

    <!-- 在項目的pom.xml中 配置私服的倉庫地址,可以將項目打jar包部署到私服 -->
    <distributionManagement>
        <repository>
            <id>nexus-public</id> <!-- nexus認證id -->
            <url>http://localhost:8081/nexus/content/repositories/releases</url>
        </repository>
        <snapshotRepository>
            <id>nexus-public</id> <!-- nexus認證id -->
            <url>http://localhost:8081/nexus/content/repositories/snapshots</url>
        </snapshotRepository>
    </distributionManagement>
</project>

五、IDEA中的Maven操作

5.1 創建Maven項目

創建Maven項目
image-20200616215847109
指定項目名稱和項目位置
image-20200616220455702
5.1.1 節點配置解析
節點 詳細描述
groupId 這是項目組的編號,這在組織或項目中通常是獨一無二的。 例如,一家銀行集團 com.company.bank擁有所有銀行相關項目。
artifactId 這是項目的 ID。這通常是項目的名稱。 例如,consumer-banking。 除了 groupId 之外,artifactId 還定義了 artifact 在存儲庫中的位置。
version 這是項目的版本。與 groupId 一起使用,artifact 在存儲庫中用於將版本彼此分離。 例如:com.company.bank:consumer-banking:1.0com.company.bank:consumer-banking:1.1
5.1.2 語義化版本號
  • 規則:正式穩定版本從v0.1.0開始,配套軟件公共API
  • 注意:正式版發布后不可修改,只能在下一個版本中發布新內容
版本類型 詳細描述
主要版本 當你做了不兼容的API 修改(正式版發布、架構升級)
次要版本 當你做了向下兼容的功能性新增(功能增減)
修訂版本 當你做了向下兼容的問題修正(BUG修復、查缺補漏)
5.1.3 擴展(SNAPSHOT)

在使用maven過程中,我們在開發階段經常性的會有很多公共庫處於不穩定狀態,隨時需要修改併發布,可能一天就要發布一次,遇到bug時,甚至一天要發布N次。我們知道,maven的依賴管理是基於版本管理的,對於發布狀態的artifact,如果版本號相同,即使我們內部的鏡像服務器上的組件比本地新,maven也不會主動下載的。如果我們在開發階段都是基於正式發布版本來做依賴管理,那麼遇到這個問題,就需要升級組件的版本號,可這樣就明顯不符合要求和實際情況了。但是,如果是基於快照版本,那麼問題就自熱而然的解決了,而maven已經為我們準備好了這一切。

maven中的倉庫分為兩種,snapshot快照倉庫和release發布倉庫。snapshot快照倉庫用於保存開發過程中的不穩定版本,release正式倉庫則是用來保存穩定的發行版本。定義一個組件/模塊為快照版本,只需要在pom文件中在該模塊的版本號后加上-SNAPSHOT即可(注意這裏必須是大寫),如下:

<groupId>cc.mzone</groupId>
<artifactId>m1</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>

maven會根據模塊的版本號(pom文件中的version)中是否帶有-SNAPSHOT來判斷是快照版本還是正式版本。如果是快照版本,那麼在mvn deploy時會自動發布到快照版本庫中,會覆蓋老的快照版本,而在使用快照版本的模塊,在不更改版本號的情況下,直接編譯打包時,maven會自動從鏡像服務器上下載最新的快照版本。如果是正式發布版本,那麼在mvn deploy時會自動發布到正式版本庫中,而使用正式版本的模塊,在不更改版本號的情況下,編譯打包時如果本地已經存在該版本的模塊則不會主動去鏡像服務器上下載

在maven的約定中,依賴的版本分為兩類——SNAPSHOT和RELEASE。SNAPSHOT依賴泛指以-SNAPSHOT為結尾的版本號,例如1.0.1-SNAPSHOT。除此之外,所有非-SNAPSHOT結尾的版本號則都被認定為RELEASE版本,即正式版,雖然會有beta、rc之類說法,但是這些只是軟件工程角度的測試版,對於maven而言,這些都是RELEASE版本。所以一般我們需要上傳到發布倉庫的時候可以在<version>標籤內直接寫版本即可,不需要再添加任何標籤!

5.2 IDEA關聯Maven

在IDEA中關聯本地安裝的maven,後續就可以通過idea來使用maven管理項目(我使用的aliyun倉庫)

在全局設置中關聯Maven
image-20200616222756521
Maven項目展示 (缺少test包下resources文件夾)
image-20200616224937892

5.3 IDEA創建測試包下resources文件夾

我們在使用IDEA創建Maven項目時,IDEA是沒有幫我們創建test包下的resources文件夾。但是Maven規範中是包含這個文件夾的,所以我們需要手動創建並聲明該文件夾

創建存放測試配置的文件夾
image-20200616225355373
指定文件夾名稱 (下拉框選擇resources文件夾創建即可)
image-20200616225859663
文件目錄結構展示 (完整Maven規範目錄結構)
image-20200616230042781

5.4 Maven項目目錄結構解析

注意: 項目中的創建包、創建類、執行,都與普通項目無異

目錄名稱 描述
src/main/java 用於創建包,存放編寫的源代碼(.java文件)
src/main/resources 存放項目中所需配置文件,比如:c3p0.properties
src/test/java 用於創建包,存放編寫的測試代碼(.java文件)
src/test/resources 存放項目中測試代碼所需配置文件
根目錄/pom.xml 項目對象模型(project object model),maven項目核心文件,其中定義項目構建方式,聲明依賴等

5.5 Maven項目類型

根據項目類型,在pom.xml文件中添加相應配置。

  • 項目類型分為Java項目和JavaWeb項目

  • 如果項目為Java項目需要在<project>標籤內添加 jar

  • 如果項目為JavaWeb項目需要在<project>標籤內添加 war

注意: Maven可以根據項目類型來確定打包方式,比如Java項目打包成jar包JavaWeb項目打包成war包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mylifes1110</groupId>
    <artifactId>firstmaven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--
        設置項目類型,打包方式:
        如果為Java項目則使用jar
        如果為JavaWeb項目使用war
    -->
    <packaging>jar</packaging>
<!--    <packaging>war</packaging>-->
</project>

5.6 IDEA中導入依賴

5.6.1 導入依賴須知

建好項目后,需要導入需要的jar包,要通過坐標來在倉庫中查找導入

  • 每個構件都有自己的坐標,其坐標分為groupId、artifactId、version 三種,翻譯後為項目標識、項目名稱、版本號
  • 在maven項目中只需要配置坐標,maven便會自動加載對應依賴。刪除坐標則會移除依賴
節點 描述
project 工程的根標籤。
modelVersion 模型版本需要設置為 4.0。
groupId 這是工程組的標識。它在一個組織或者項目中通常是唯一的。例如,一個銀行組織 com.companyname.project-group 擁有所有的和銀行相關的項目。
artifactId 這是工程的標識。它通常是工程的名稱。例如,消費者銀行。groupId 和 artifactId 一起定義了 artifact 在倉庫中的位置。
version 這是工程的版本號。在 artifact 的倉庫中,它用來區分不同的版本。例如:com.company.bank:consumer-banking:1.0 com.company.bank:consumer-banking:1.1
5.6.2 查找依賴

依賴查找服務需要在如下網址中查找依賴,獲取依賴坐標后,在maven項目中的pom.xml文件中導入

  • 依賴查找服務地址: https://mvnrepository.com/
查找jar文件
image-20200616111418839
jar文件的選擇
image-20200616112444879
Copy依賴坐標
image-20200617003002005
5.6.3 導入依賴

在項目的pom.xml文件中添加依賴

  • 首先添加<dependencies>標籤
  • 最後添加複製好的依賴坐標

注意: 導入依賴可以導入多個所需jar文件的依賴,依次在<dependencies>標籤內添加依賴坐標即可

導入依賴
image-20200617003853705
5.6.4 同步依賴

導入依賴后,你會發現pom.xml文件中的依賴坐標是紅色報錯的,而在IDEA的右下角有那麼一個框。這時候需要點擊框內提示信息在maven倉庫中同步下載依賴到項目中

同步下載依賴到項目中
image-20200617004515463
5.6.5 查看依賴

導入並同步下載好的依賴,可以通過項目和IDEA中Maven控制面板查看

查看依賴
image-20200617005635304

5.7 基於Maven創建Web項目

5.7.1 修改web項目打包方式

pom.xml文件中設置 <packaging>war/packaging>修改打包方式為web項目

5.7.2 導入web依賴

基於Maven項目,我們導入了maven所需依賴,如果創建web項目的話,還需要導入JSPServletJSTL依賴,使Maven項目具有web編譯環境。(在pom.xml文件中添加以下依賴)

        <dependency>
            <!-- jstl 支持 -->
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <!-- servlet編譯環境 -->
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <!-- jsp編譯環境 -->
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>

image-20200617123210482

5.7.3 創建web項目目錄結構

web項目在IDEA中有web規範目錄文件夾的,比如webapp、WEB-INF、web.xml、index.jsp。而由於Maven項目中沒有該規範目錄,這就需要我們自己創建目錄結構了!

創建web項目目錄結構有以下注意點:

  • webapp文件夾必須是基於main目錄下創建的,與java文件夾同級
  • webapp文件夾IDEA自動識別此文件夾,給與了特殊藍色圓點標識
  • 在WEB-INF目錄下創建的的web.xml是一個空的xml文件,我們需要在該文件中鍵入如下web.xml空白模板信息,只需要將下面的模板複製到項目中的web.xml文件中即可!
  • 創建的文件夾以及文件名稱千萬不要寫錯!

注意: 在創建項目的時候,其實我們是可以指定使用IDEA來創建來創建web項目的目錄結構的,創建項目時需要勾選Create from archetype。只是IDEA為我們構建的項目架構是有版本差異的,而且還附加了很多對我們無用的註解等等,所以我們一般都手動創建,IDEA自動構建作為了解就好!

xml空白模板

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
</web-app>
IDEA自動構建項目結構
image-20200617164915737
手動構建web項目結構
基於main目錄下創建webapp文件夾
image-20200617124249878
image-20200617124319926
基於webapp目錄創建WEB-INF文件夾
image-20200617124624648
基於WEB-INF目錄創建web.xml文件
image-20200617125038803
image-20200617125055216
xml文件內容展示
image-20200617125729115
基於webapp目錄創建index.jsp文件
image-20200617125613861
目錄展示 (完整的web項目目錄結構)
image-20200617125808227
5.7.4 tomact的引入

關於Tomact服務的引入,需要我們手動添加tomact服務

添加tomact服務后,如果對tomact服務器在IDEA中的開發流程不熟悉的小夥伴,不要灰心。請參考tomact服務器基礎和開發步驟即可,此文章中詳細講到了關於tomact的各種知識點!

添加tomact服務
image-20200617131801103

5.8 pom文件的其他標籤操作

5.8.1 build標籤修改默認打包名

默認的打包名稱:artifactid+verson.打包方式

我們可以通過build中finalName修改,如下操作:

<build>
  <finalName>maven_name</finalName>
</build>  
5.8.2 引入插件

dependencies引入開發需要的jar包,有時候我們還需要引入一些常用的插件,比如:jdk、tomact、分頁插件等等

插件配置位置也是在<build>標籤中,如下:

<build>
<plugins>
    <!-- java編譯插件,配jdk的編譯版本 -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
      </configuration>
    </plugin>
    <!-- tomcat插件 -->
    <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <configuration>
        <port>8080</port>
        <path>/</path>
        <uriEncoding>UTF-8</uriEncoding>
        <server>tomcat7</server>
      </configuration>
    </plugin>
  </plugins>
</build>
5.8.3 控制打包資源

如果在java文件夾中添加java類,會自動打包編譯到classes文件夾下。但是xml文件默認不會被打包,需要我們手動指定打包。可以使用<resources>標籤來指定要打包資源的文件夾要把哪些靜態資源打包到classes根目錄下

<!--打包指定靜態資源-->
<build>
<resources>
    <resource>
      <!-- 指定要打包資源的文件夾 要把哪些靜態資源打包到 classes根目錄下-->
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.xml</include>
        <include>**/
*.properties</include>
      </includes>
    </resource>
    <resource>
      <directory>src/main/resources</directory>
      <excludes>
        <exclude>spring/*</exclude>
      </excludes>
      <includes>
        <include>*.xml</include>
        <!--<include>*/
*.properties</include>-->
      </includes>
    </resource>
  </resources>
</build>

六、依賴的生命周期

6.1 什麼是依賴的生命周期

jar文件的生效時間段可以成為依賴的生命周期

6.2 依賴的生命周期分類與詳解

前三個是常用的依賴生命周期設置,而後兩個製作了解即可,幾乎用不到!

標識 周期
compile 缺省值(默認依賴生命周期),適用於所有階段(測試運行,編譯,運行,打包)
provided 類似compile,期望JDK、容器或使用者會提供這個依賴。如servlet-api.jar;適用於(測試運行,編譯)階段
test 只在測試時使用,適用於(編譯,測試運行)階段,如 junit.jar
runtime 依賴在編譯器不使用,只在運行時使用,如 mysql的驅動jar,適用於(運行,測試運行)階段
system Maven不會在倉庫中查找對應依賴,在本地磁盤目錄中查找;適用於(編譯,測試運行,運行)階段

6.3 依賴生命周期的使用

關於依賴生命周期的使用,需要在期望指定生命周期的依賴內添加<scope>標籤,在此標籤內添加所需依賴標識。

complie

jstl依賴默認沒有compile標識的生命周期。因為在依賴中不指定生命周期就是默認指定適用於所有階段的生命周期,其默認標識為compile。

image-20200617135847510

provided

servlet和jsp依賴默認指定provided標識的生命周期。因為我們在servlet或jsp代碼時是需要這兩個依賴的,但是我們將項目部署到tomact中,本地tomact目錄的lib文件夾下也會有一些jar文件,所以這造成了一種依賴衝突。為了避免這種依賴衝突我們需要指定依賴的生命周期為編譯和測試運行階段。這樣我們在書寫代碼時,編譯期也有有依賴可以使用,不會飄紅,而在過了編譯期後項目部署到了tomact中,該依賴聲生命就會被結束掉了,不會影響tomact服務器內置依賴的使用!

image-20200617135818392

test

在maven項目中,項目結構是區分main主文件和test測試文件的。如果我們在使用Junit單元測試時,指定依賴的生命周期為test,那該依賴只適用於test測試文件內,在其他文件的階段默認沒有單元測試的依賴。

簡單來說,在test文件夾內創建的測試類,使用@Test註解不會有任何問題。如果換做在main文件夾和其他文件夾中創建測試類,使用@Test註解就會因沒有依賴注入而報錯。

image-20200617140515452

runtime

我們在添加mysql驅動的依賴時,你會發現它並沒有指定生命周期為runtime。這是因為我們在書寫jdbc工具類的操作時,如果在編譯期沒有mysql驅動的依賴,它並不會飄紅報錯。如果沒有依賴只有在我們運行的時候才會發生報錯,並告知mysql驅動依賴未找到。所以,這就顯得runtime這個依賴生命周期十分的雞肋。因此,可以不指定該生命周期。

image-20200617141145812

system

當依賴的生命周期設置為system時,表示該依賴項是我們自己提供的,不需要Maven到倉庫裏面去找。
指定scope為system需要與另一個屬性元素systemPath一起使用,它表示該依賴項在當前系統的位置,使用的是絕對路徑。由於此類依賴不是通過 Maven 倉庫解析的,而且往往與本機系統綁定,可能造成構建的不可移植,因此應該慎用。systemPath 元素可以引用環境變量。

image-20200617142301138

七、maven的構建命令

7.1 項目的構建過程

7.2 常用構建命令

一般命令的鍵入在IDEA中的框中鍵入命令就可以!

image-20200617170717904

命令 描述
mvn compile(常用) 編譯項目,生成target文件
mvn package(常用) 打包項目,生成war或jar文件
mvn clean(常用) 清理編譯或打包后的項目結構
mvn install(常用) 打包後上傳到Maven本地倉庫
mvn source:jar 打包項目,生成jar包
mvn deploy 只打包,不測試
mvn site 生成站點
mvn test 執行測試源碼

7.3 maven項目的生命周期

maven項目的生命周期分為了三個階段,而這三個階段相互獨立、互不影響

  1. 清理生命周期(Clean Lifecycle): 該生命周期負責清理項目中多餘信息,保持資源和代碼的整潔性。一般用來清空目錄下的文件
  2. 默認構建生命周期(Default Lifeclyle): 該生命周期表示項目的構建過程,其中定義了一個項目構建要經歷的不同階段
  3. 站點管理生命周期(Site Lifecycle): site生命周期的目的是建立和發布項目站點,Maven能夠基於POM所包含的信息,自動生成一個友好的站點

clean

該生命周期主要是對項目編譯生成的文件進行清理,清理的話主要是清理編譯後項目中的target文件夾,清理后還可以通過編譯指令重新生成。

  • 命令: mvn clean

default

該生命周期的主要目的是項目的編譯 -> 測試 -> 打包 -> 發布

  • 命令: mvn deploy
  • 其中default生命周期分為23個階段,如下列舉比較重要的幾個階段
  • validate:驗證工程是否正確,所有需要的資源是否可用。
  • compile:編譯項目的源代碼。
  • test:使用合適的單元測試框架來測試已編譯的源代碼。這些測試不需要已打包和布署。
  • Package:把已編譯的代碼打包成可發布的格式,比如jar。
  • integration-test:如有需要,將包處理和發布到一個能夠進行集成測試的環境。
  • verify:運行所有檢查,驗證包是否有效且達到質量標準。
  • install:把包安裝到maven本地倉庫,可以被其他工程作為依賴來使用。
  • Deploy:在集成或者發布環境下執行,將最終版本的包拷貝到遠程的repository,使得其他的開發者或者工程可以共享。

site

該生命周期的主要是建立和發布項目站點,Maven能夠基於POM所包含的信息,自動生成一個友好的站點

  • 命令: mvn site

注意: 低版本的site插件可能引發失敗現象!升級高版本site插件即可!

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-site-plugin</artifactId>
  <version>3.7.1</version>
</plugin>

7.4 maven命令與插件的關係

maven命令是操作maven項目的重要方式,但是我們要知道maven命令只是支配maven插件工作的一個方式,其工作核心主要還是maven插件。maven內嵌了項目操作的插件,我們只需要通過執行命令來調用插件來完成項目的編譯、測試、發布等工作

注意: 執行一次命令可能會觸發多個插件工作

八、傳遞依賴和衝突解決

8.1 什麼是傳遞依賴

假如有Maven項目A,項目B依賴A,項目C依賴B。那麼我們可以說 C依賴A。也就是說,依賴的關係為:C – > B – > A, 那麼我們執行項目C時,會自動把B、A都下載導入到C項目的jar包文件夾中,這就是依賴的傳遞性。

8.2 什麼是依賴衝突

依賴衝突我在上文中也提到過,依賴衝突是當直接引用或者間接引用出現了相同的jar包擁有不同版本的時候。

舉個例子:A依賴於B,B依賴於C,此時C的版本為V1.0;如果此時引入的C依賴還有一個V2.0,那麼我們的A傳遞依賴於C,此時C的版本為V2.0。這時候就是一個衝突,直接或間接的都引用了C,而C版本有兩個!

8.3 手動解決依賴衝突

如果我不想在C依賴中出現B,那麼就可以主動的使用依賴排除技術,排除B依賴的引用,如下操作需要添加排除依賴的配置信息(<exclusions>標籤和其中信息,其中兩個id標籤是需要排除依賴的id):

     <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.12.RELEASE</version>

            <!--手動排除依賴-->
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>

        </dependency>
     </dependencies>

8.4 自動解決依賴衝突

8.4.1 自動解決依賴衝突的途徑

自動解決依賴通途問題的兩個途徑分為:短路優先原則先聲明優先原則

8.4.2 短路優先原則

首先,先看如下依賴關係

  1. A – > B – > C – > D – > E(V1.0)
  2. A – > F – > E(V2.0)

解釋: 1中,A依賴於B,B依賴於C,C依賴於D,D依賴於E,此時E的版本為V1.0版本(2中解釋基本相似)

所謂短路優先,就是路徑段的依賴有限被加載使用,這裏我們可以看到2中的路徑是最短的路徑,所以maven在加載依賴的時候,是使用2中的這一條依賴和版本

8.4.3 先聲明優先原則

針對依賴路徑長度相同的情況,則使用先聲明優先原則,看如下依賴關係

  1. A – > B – > C(V1.0)
  2. A – > D – > C(V2.0)

此時路徑優先原則不能解決問題時,maven需要判斷在A項目的<depencies>標籤內,B和D的哪個依賴聲明在前面,如果B依賴聲明早於D依賴,那麼就使用1中的這一條依賴和版本

微信公眾號 本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司“嚨底家”!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

改變飲食減少水足跡 研究:吃素能省55%用水

摘錄自2018年9月11日中央社倫敦報導

最新研究指出,奉行較健康的飲食習慣可減少個人水足跡(water footprint),降低肉類攝取量可節省至少10%用水量,吃素更能減少多達55%用水量。

英國廣播公司(BBC)報導,這項在英國、法國和德國進行的研究,刊登於「自然永續」(Nature Sustainability)期刊。

研究人員表示,轉向健康飲食是個「雙贏局面」,不僅能讓身體更健康,且可用更少的水資源來生產食物。飼養牲畜耗費大量用水,生產油類、糖類及脂肪也需要大量水資源,但種植水果和蔬菜較省水。

但這項研究承認,鼓勵民眾改變飲食習慣並不容易,需要採取一連串干預措施,例如向不健康食品課稅和印製更清楚的食品標示。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

聚甘新